一、什么是单链表
链表由一系列的节点组成,是通过节点的指针链接次序进行存储数据的一种数据结构。
和顺序表一样,链表也是线性表的一种。我们同样可以通过链表来实现通讯录项目的实现。链表分为单链表和双链表,今天我们主要学习单链表。
在逻辑结构上顺序表和链表都是连续的,但在物理结构上顺序表是连续的而链表是非连续的。
前文我们使用顺序表实现了通讯录项目,让我们仔细思考一下是否存在一些问题呢?比如多次的插入删除会导致程序运行效率低下,同时realloc动态增容的不确定性会导致空间浪费。而链表是一个个独立的存储空间从而避免这些问题。下面我们为进行单链表的实现。
二、单链表的初始化和打印
定义两个变量:存储的数据(SLTDataType data)和指向下一个节点的指针(strcut SListNode* node)。
这里通过typedef重命名int(SLTDataType),结构体SListNode(SLTNode)。用malloc创建节点空间,通过指针将节点连接起来,记住将最后一个指针置为NULL。
创建SLTNode* plist变量以便于接下来实参传递。
三、单链表功能的实现
所需功能实现的函数
提前说明:plist是test.c文件中定义的结构体变量实参,pphead是SList.h和SList.c的形参,pcur是为了防止pphead位置改变的变量。
假如尾插的是空链表,形参phead的改变(0变为NULL)不会形象实参plist的改变(仍为0),需要&plist(传一级指针的地址)。
在实现函数功能之前我们需要区分*plist plist &plist
第一个节点 *plist **pphead
指向第一个节点的指针 plist *pphead
指向第一个节点的指针的地址 &plist pphead
1.尾插及空间申请
封装申请空间函数SLTBuyNode(x),定义新变量newnode,后用malloc申请空间,成功后及时将newnode -> next置为空。
这里先assert(pphead)不能对空指针解引用,但链表可以为空。调用SLTBuyNode(x)函数,判断指针是否为空,空perror,非空定义SLTNode* ptail = *pphead从头节点开始遍历直到遇见NULL
2.头插
assert(pphead)创建newnode变量申请空间后新节点指向原第一个节点,指向原第一个节点的指针指向新节点。
3.尾删
assert(pphead&&*pphead)不能是空链表,不然删谁。假如链表只有一个节点直接free(),若链表有多个节点就创建两个变量prev和ptail,让prev=ptail,ptail=ptail->next,直到ptail->next为空时再free(ptail)。
4.头删
和尾删类似,不过只删一个节点。
5.查找
创建pcur方便后续的更改,不会改变pphead的地址。
6.指定位置插入数据
assert(pphead&&*pphead&&pos),所传位置指针也不能为空。如果pos=*pphead,直接头插反之用prev指针的next找到pos,形成prev->newnode->(pos)的指针传递顺序。
7.指定位置之后插入数据
assert(pos).这里有两种方法:法一:pos -> next = newnode; newnode -> next = pos -> next;
法二:newnode -> next = pos -> next; pos -> next = newnode;只能选法二,因为先pos -> next = newnode;会使得原pos -> next地址丢失或者定义一个next指针保存原pos -> next的地址再用法一
8.指定位置删除数据
判断pos==*pphead,是即头删,否则就需要前置节点和后置节点,形成(prve -> next) -> (pos -> next)的指针传递顺序。
9.指定位置之后删除数据
assert(pos&&pos->next),借助del变量=pos->next,形成pos -> del ->(del->next)的指针传递顺序,free(del)。
注意:*(解引用)的优先级低于->(结构体指针访问操作符),同时使用要带()。
四、单链表的销毁
链表不能为空并且指向链表的指针也不能为空。assert(pphead&&*pphead)。通过循环指向下一个节点的指针置为空并不断的释放下一个节点。注意:头节点被释放要及时置为空,否则就是野指针。
五、完整代码
SList.h
SList.c
test.c