文章目录
一、单链表的概念与结构
1.单链表的概念
链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的,我们可以使用生活中的例子打个简单的比喻,如图:
举的例子就是我们生活中的火车,淡季时⻋次的⻋厢会相应减少,旺季时⻋次的⻋厢会额外增加⼏节,只需要将⽕⻋⾥的某节⻋厢去掉/加上,不会影响其他⻋厢,每节⻋厢都是独⽴存在的
链表其实大致也是这样,它的每个节点都通过next指针连接起来,每个节点看起来就像一个又一个车厢,整个链表看起来就像一辆火车,如图:
在真正的链表中也是前后互相连接的,只是它们连接起来的依据是地址,前一个节点通过存储后一个节点的地址来找到后一个节点,这就涉及到了我们链表中单个节点的结构了,我们接下来继续来学习
2.单链表的节点
与顺序表不同的是,链表⾥的每节"⻋厢"都是独⽴申请下来的空间,我们称之为“节点”,结点的组成主要有两个部分:当前结点要保存的数据和保存下⼀个结点的地址(指针变量),这样我们的一个节点就可以在保存数据的基础上,通过它所存储的下一个节点的地址找到它的下一个节点
链表不像顺序表那样,直接定义出一个确定的结构,链表是由一个一个节点组成,所以我们需要定义的是节点的结构,前节点和后节点建立一定的关系,这样所有节点组合起来就抽象出来了我们的链表
接着我们来看单链表一个节点的结构是怎么定义的,如下:
typedef int SLDateType;
typedef struct SListNode
{
SLDateType data;
struct SListNode* next;
}SLTNode;
在我们定义的这个节点结构中,有两个成员,一个是我们所存放的数据data,由于我们一个节点的下一个节点也是一个这样的结构体,所以指向下一个节点的指针是一个结构体指针,也就是类型为struct SListNode*的指针
由于我们现在要实现的是单链表,所以它的节点名称就可以叫做SListNode,其中S是single的缩写,翻译出来就是单链表,为了以后不用每次使用这个结构体就要加上struct,我们就又给它取了一个别名SLTNode方便使用
然后由于我们不知道要存放的数据是什么类型,所以我们这里typedef一个类型来取代,以后如果想要更改类型只需要在这里更改
我们再次总结一下链表节点的特点,链表中每个结点都是独⽴申请的(即需要插⼊数据时才去申请⼀块结点的空间),我们需要通过指针变量来保存下⼀个结点位置才能从当前结点找到下⼀个结点
3.链表的性质
链表和顺序表从本质上有所区别,它们都属于线性表,也就是在逻辑上它们都是连续的,但是物理上一个连续一个不一定连续,接下来我们就来总结一下关于链表自己的一些性质:
- 链表属于链式结构,它在逻辑上是连续的,在物理结构上不⼀定连续
- 结点⼀般是从堆上申请的,也就是链表的节点是malloc来的
- 从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续,所以链表逻辑上可能是不连续的
- 当我们想要保存⼀个整型数据时,实际是向操作系统申请了⼀块内存,这个内存不仅要保存数据,也需要保存下⼀个结点的地址(当不存在下⼀个结点时,它的next指针保存的时空指针)
- 当我们想要访问链表的所有数据时,只需要得到头结点即可,根据头结点的next指针可以找到下一个节点,下一个节点也可以通过自己的next指针找到再下一个节点,所以得到头结点我们就可以访问整个链表
二、单链表的实现
1.结构准备
在我们实现链表的各种方法之前,我们需要在我们的SList.h定义好单链表的结构,然后把需要用到的头文件包含一下,然后再在上面我们也已经介绍过了,这里直接给出代码:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLDateType;
typedef struct SListNode
{
SLDateType data;
struct SListNode* next;
}SLTNode;
2.链表的打印和节点申请
为了方便我们调试以及插入节点,我们要先解决链表的打印和节点申请,而我们在使用链表时,一般都是直接在主函数中创建一个叫plist的节点指针,把它当作链表的头结点,它只是一个指针,最开始初始化成空指针即可,不需要专门写一个初始化函数,在我们插入数据后,它就指向我们的头结点
打印函数
其实链表的打印很简单,我们之前也讲过,只要知道一个链表的头结点就可以访问整个链表,这里我们函数只需要接收一个链表的头节点,然后对它进行打印,我们取的函数名为SLTPrint
具体方法就是,创建一个节点指针pcur指向我们的头结点,然后创建一个循环,只要pcur不为空,那么就打印pcur指向的节点的数据,然后让pcur走到下一个节点,也就是pcur = pcur->next
然后跳出循环后再打印一个NULL,因为单链表的最后一个节点之后是空指针,为了体现我们打印到了最后,我们就再打印一个NULL
最后我们还要注意一个点,由于后面我们要实现的方法函数都是传的二级指针,虽然打印函数传一级指针就够了,但是为了保持我们传参的一致性,所以这里我们打印函数还是传二级指针,也就是头结点的地址,函数具体代码如下:
void SLTPrint(SLTNode** pphead)
{
assert(pphead);
SLTNode* pcur = *pphead;
while (pcur)
{
printf("%d -> ", pcur->data);
pcur = pc