【数据结构】单链表的实现

news/2024/12/3 22:55:53/

🌇个人主页:平凡的小苏

📚学习格言:别人可以拷贝我的模式,但不能拷贝我不断往前的激情

🛸C语言专栏:https://blog.csdn.net/vhhhbb/category_12174730.html

🚀数据结构专栏:https://blog.csdn.net/vhhhbb/category_12211053.html

        家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我真重要,各位路过的友友麻烦多多点赞关注,欢迎你们的私信提问,感谢你们的转发!

        关注我,关注我,关注我,你们将会看到更多的优质内容!!

 

目录

 1、单链表存储结构定义

2、单链表的优缺点

3、单链表的实现 

3.1、线性单链表的存储结构

3.2、单链表的插入

3.3、单链表的删除 

3.4、单链表打印

3.5、单链表查找

3.6、单链表的销毁

4、源码

4.1、SList.h 

4.2、SList.c

4.3、Test.c


 

 1、单链表存储结构定义

  • 线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以是不连续的。这就意味着,这些数据元素可以存在内存未被占用的任意位置。
  • 以前在顺序结构中,每个数据元素只需要存储数据元素信息就可以了。现在链式结构中,除了要存储数据元素信息外,还要存储它的后继元素的存储地址。
  • 因此,为了表示每个数据元素ai与其直接后继数据元素a(i+1)之间的逻辑关系,对数据元素ai来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。指针域中存储的信息称作指针。这两部分信息组成数据元素ai的存储映像,称为结点。
  • n个结点(ai的存储映像)链结成一个链表,即为线性表(a1,a2,......,an)的链式存储结构,因为此链表的每个结点中只包含一个指针域,所以叫做单链表。

2、单链表的优缺点

简单的对单链表结构和顺序存储结构做对比:

存储分配方式:

        1.顺序存储结构用一段连续存储单元依次为存储线性表的数据元素

        2.单链表采用链式存储结构,用一组任意的存储单元存放线性表的元素

时间性能:

        1.查找:顺序存储结构O(1)

                      单链表O(n)

        2.插入和删除:

                       顺序存储结构需要平均移动表长一半的元素,时间复杂度为O(n)

                       单链表在找出位置的指针后,插入和删除时间复杂度仅为O(1)]

空间性能:

        1.顺序存储结构需要预分配存储空间,分小了,易发生上溢,分大了,浪费

        2.单链表不需要分配存储空间,只要有就可以分配,元素个数也不受限制

结论:

1.若线性表需要频繁查找,很少进行插入和删除操作时,宜采用顺序存储结构。

2.当线性表中的元素个数变化较大或者根本不知道有多大时,最好使用单链表。

3、单链表的实现 

3.1、线性单链表的存储结构

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int SLTDateType;
typedef struct SListNode
{SLTDateType data;//数据域struct SListNode* next;//指针域
}SListNode;

3.2、单链表的插入

SListNode* BuySListNode(SLTDateType x)//申请结点函数
{SListNode* new = (SListNode*)malloc(sizeof(SListNode));if (new == NULL){perror("malloc fail:");exit(-1);}new->data = x;new->next = NULL;return new;
}
void SListInsert(SListNode** pplist, SListNode* pos, SLTDateType x)
{assert(pplist);assert(pos);SListNode* new = BuySListNode(x);SListNode* prev = *pplist;if (*pplist == pos)//pos处于单链表的头就是头插{new->next = *pplist;*pplist = new;}else{while (prev->next != pos)//处于pos的前面插入一个数{prev = prev->next;}new->next = pos;prev->next = new;}
}

算法思路:

(1)声明——指针pplist指向链表头;

(2)当prev->next不等于pos时,让p的指针向后移动,不断指向下一结点;

(3)pos不等于空就代表这个结点存在,若不存在就断言;

(4)单链表插入的标准语句new-> = pos; prev->next = new;

注:

在这段算法代码中,我们用到了c语言的malloc标准函数,它的作用就是生成一个新的结点,其类型与Node是一样的,其实质就是在内存中找了一小块空地,准备用来存放数据。

3.3、单链表的删除 

        现在我们来看看单链表的删除,设存储元素data的结点为pos,要实现将结点pos删除单链表的操作,其实就是将他的前继结点的指针绕过,指向它的后继结点即可。

实现代码如下:

void SListErase(SListNode** pplist, SListNode* pos)
{assert(pplist);assert(*pplist);assert(pos);SListNode* prev = *pplist;SListNode* cur = *pplist;while (cur){if (cur == pos){if (*pplist == pos)//如果pos处于链表头那就代表是头删{*pplist = cur->next;free(cur);cur = *pplist;}else//将它的前继结点指向的指针绕过,指向它的后继结点{prev->next = cur->next;free(cur);cur = prev->next;}}else{prev = cur;cur = cur->next;}}
}

算法思路:

(1)声明——指针pplist指向链表头;

(2)当cur不等于pos时,就往后遍历链表,让cur的指针向后移动,不断指向下一个结点

(3)若pos等于空,就代表这个链表不存在;

(4)单链表删除的标准语句 prev->next = cur->next;  free(cur); cur = prev->next;

注;

这一段算法代码里,我们又用到了另一个C语言的标准函数free。它的作用就是让系统回收一个Node结点,释放内存。

3.4、单链表打印

void SListPrint(SListNode* plist)
{SListNode* cur = plist;while (cur)//遍历到空就停止{printf("%d ", cur->data);cur = cur->next;}printf("\n");
}

3.5、单链表查找

SListNode* SListFind(SListNode* plist, SLTDateType x)
{SListNode* cur = plist;while (cur){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}

3.6、单链表的销毁

void SListDestroy(SListNode** pplist)
{free(*pplist);*pplist = NULL;
}

4、源码

注:由于头插、头删、尾插、尾删和任意位置的插入和删除是差不多,这里直接放源码

4.1、SList.h 

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int SLTDateType;
typedef struct SListNode
{SLTDateType data;struct SListNode* next;
}SListNode;// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SListInsert(SListNode ** pplist,SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListErase(SListNode ** pplist,SListNode* pos);
// 单链表的销毁
void SListDestroy(SListNode**pplist);

4.2、SList.c

#include"SList.h"
SListNode* BuySListNode(SLTDateType x)
{SListNode* new = (SListNode*)malloc(sizeof(SListNode));if (new == NULL){perror("malloc fail:");exit(-1);}new->data = x;new->next = NULL;return new;
}
void SListPushBack(SListNode** pplist, SLTDateType x)
{assert(pplist);SListNode* new = BuySListNode(x);SListNode* tail = *pplist;if (*pplist == NULL){*pplist = new;}else{while (tail->next != NULL){tail = tail->next;}tail->next = new;}
}
void SListPrint(SListNode* plist)
{SListNode* cur = plist;while (cur){printf("%d ", cur->data);cur = cur->next;}printf("\n");
}
void SListPushFront(SListNode** pplist, SLTDateType x)
{assert(pplist);SListNode* new = BuySListNode(x);new->next = *pplist;*pplist = new;
}
void SListPopBack(SListNode** pplist)
{assert(pplist);assert(*pplist);SListNode* prev = NULL;SListNode* tail = *pplist;if ((*pplist)->next == NULL){free(*pplist);*pplist = NULL;}else{while (tail->next){prev = tail;tail = tail->next;}prev->next = NULL;free(tail);tail = NULL;}
}
void SListPopFront(SListNode** pplist)
{assert(pplist);assert(*pplist);SListNode* cur = (*pplist)->next;free(*pplist);*pplist = cur;
}
SListNode* SListFind(SListNode* plist, SLTDateType x)
{SListNode* cur = plist;while (cur){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}
void SListInsert(SListNode** pplist, SListNode* pos, SLTDateType x)
{assert(pplist);assert(pos);SListNode* new = BuySListNode(x);SListNode* prev = *pplist;if (*pplist == pos){new->next = *pplist;*pplist = new;}else{while (prev->next != pos){prev = prev->next;}new->next = pos;prev->next = new;}
}
void SListErase(SListNode** pplist, SListNode* pos)
{assert(pplist);assert(*pplist);assert(pos);SListNode* prev = *pplist;SListNode* cur = *pplist;while (cur){if (cur == pos){if (*pplist == pos)//如果pos处于链表头那就代表是头删{*pplist = cur->next;free(cur);cur = *pplist;}else//将它的前继结点指向的指针绕过,指向它的后继结点{prev->next = cur->next;free(cur);cur = prev->next;}}else{prev = cur;cur = cur->next;}}
}
void SListDestroy(SListNode** pplist)
{free(*pplist);*pplist = NULL;
}

4.3、Test.c

#include"SList.h"
void TestSListNode()
{SListNode* plist = NULL;SListPushFront(&plist, 1);SListPushFront(&plist, 2);SListPushFront(&plist, 3);SListPushFront(&plist, 4);SListPushFront(&plist, 5);SListNode* pos = SListFind(plist, 3);SListErase(&plist,pos);SListPrint(plist);}
int main()
{TestSListNode();return 0;
}

好了!我的分享到这里就结束了,有什么不足的地方请各位大佬多多指教!!!


http://www.ppmy.cn/news/29853.html

相关文章

数据结构(一)(嵌入式学习)

数据结构干货总结&#xff08;一&#xff09;基础线性表的顺序表示线性表的链式表示单链表双链表循环链表循环单链表循环双链表栈顺序存储链式存储队列队列的定义队列的常见基本操作队列的顺序存储结构顺序队列循环队列队列的链式存储结构树概念二叉树二叉树的创建基础 数据&a…

【基础算法】双指针---最长连续不重复子序列

&#x1f339;作者:云小逸 &#x1f4dd;个人主页:云小逸的主页 &#x1f4dd;Github:云小逸的Github &#x1f91f;motto:要敢于一个人默默的面对自己&#xff0c;强大自己才是核心。不要等到什么都没有了&#xff0c;才下定决心去做。种一颗树&#xff0c;最好的时间是十年前…

成功解决configure: error: the HTTP rewrite module requires the PCRE library

文章目录 前言问题复现问题解决思考环节总结前言 大家好,我是沐风晓月,本专栏是记录日常实验中的所有疑难杂症,教程的安装,程序的bug,甚至各类报错,如果你也遇到了困惑和问题,欢迎与我一起交流学习。 另外不要解决完问题就结束了,思考环节也要好好看看哦。 问题复现…

移除元素(每日一题)

目录 一、题目描述 二、题目分析 2.1 方法一 2.1.1 思路 2.1.2 代码 2.2 方法二 2.2.1 思路 2.2.2 代码 一、题目描述 题目链接&#xff1a;27. 移除元素 - 力扣&#xff08;LeetCode&#xff09; 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数…

python实现PCA降维画分类散点图并标出95%的置信区间

此代码以数据集鸢尾花为例&#xff0c;对其使用PCA降维后&#xff0c;绘制了三个类别的样本点和对应的置信圆&#xff08;即椭圆&#xff09;。先放效果图。 下面是完整代码&#xff1a; from matplotlib.patches import Ellipsedef plot_point_cov(points, nstd3, axNone, **…

Protobuf简介

Protobuf简介 1. Protocol Buffers1.1. 什么是Protocol Buffers?1.2. 选择你最喜欢的语言1.3. 如何开始2. Protocol Buffer Basics: C++2.1. 问题领域2.2. 在哪里找到示例代码2.3. 定义协议格式(Defining Your Protocol Format)1. Protocol Buffers Protocol Buffers(协议缓冲…

颠覆你的认知,业务同事都能开发软件,我简直无地自容……

经常看到网络鼓吹业务人员也能搭建应用&#xff0c;本是嗤之以鼻、半信半疑&#xff0c;但当这件事真实发生在自己身上时&#xff0c;竟觉得此言不虚&#xff1f; 一、背景 最近公司为了集成系统、提升扩展能力&#xff0c;引进了低代码平台JNPF&#xff0c;说个题外话&#…

基于容器云提交spark job任务

容器云提交spark job任务 容器云提交KindJob类型的spark任务&#xff0c;首先需要申请具有Job任务提交权限的rbac&#xff0c;然后编写对应的yaml文件&#xff0c;通过spark内置的spark-submit命令&#xff0c;提交用户程序(jar包)到集群执行。 1、创建任务job提交权限rbac …