目录
一.什么是链表?它可以吃吃吗?
在上一篇文章中我们学习了顺序表,但是顺序表要求数据连续存储,并且头插头删时需要移动数据导致时间复杂度过大。为了解决顺序表的缺陷,链表应运而生。它是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序通过链表中的指针链接次序实现。插入或删除数据时,链表并不需要挪动数据,而只需要找到相应的位置。形象地来说,链表就像一条链子,只要找到了头部也就找到了所有数据。
二.链表的实现
注意:以下的pphead表示头指针的指针,phead表示头指针(也就是链表头),pos表示操作的位置。在函数的实现中,我们应尽量不改变phead与pphead,这样才能“记住”链表头。
1.链表结构体定义
下面我们用两张图来形象地说明链表的结构,方框上的十六进制数字表示结构体对象的地址。
链表就像火车,一个一个的结点就是车厢,通过指针链接。每个结点包含自身的数据信息和下一个结点的地址,最后一个结点保存的地址为NULL。因此,我们可以这样定义结构体:
注:之所以重定义int,是为了当结构体数据类型改变时更容易维护代码。
2.创建结点
注:我们使用一个临时变量tmp来接收开辟的空间,若开辟失败则程序提前终止。另外,新结点的指针应该初始化为NULL。
3.创建链表
创建链表最重要的部分是把各个结点链接起来。但是为了保存链表头,我们不能用phead链接,而要另外创建一个指针ptail进行链接。这里为了方便,我就直接赋值0~n-1啦~~
4.打印链表
同样地,我们需要一个新指针去遍历链表。
5 .尾插(在尾部插入数据)
注意:此处必须传二级指针!因为若头指针为空,尾插相当于从无到有创建一个新数据,那么头指针应该指向这个新数据。那么为了改变头指针,我们传参时应该传头指针的指针,也就是二级指针pphead。若头指针不为NULL,那么需要找到尾巴再插入。
6.尾删(在尾部删除数据)
注意,若链表中只有一个结点,那么直接删除此结点即可。此时要把头指针置为空,因此要传二级指针。为了不改变*pphead,我们创建一个新指针ptail去找到链表的尾巴。另外,此处地75行若直接判断ptail->next为空后释放ptail,会导致ptail前一个结点的next变成野指针,这是很危险的。因此我们应该判断ptail->next->next,再free(ptail->next)并且置空,这样相当于间接找到了尾巴的前一个结点!!!!
7.头插(在头部插入数据)
这个函数较为简单,只要创建新结点后链接,并把新结点作为头结点即可。传二级指针的原因也是因为要改变头指针。
8.头删(删除头部数据)
注意:第92行是为了保存*pphead->next并把它作为新的头结点地址。
9.寻找数据
10.结点后插入数据
注意:初学者容易犯把第111行和第112行交换的错误。如果这样,那么 newnode->next将会指向newnode自己!!!!无法链接!!!!
11.结点后删除数据
12.结点处插入数据
说明:若pos==*pphead,相当于头插。否则我们要利用prev找到结点前一个数据,并且把结点前一个数据、新结点和结点三者链接起来。
13.结点处删除数据
注意:类似上一个函数,若pos==*pphead相当于头删
14.销毁链表
好啦!!以上就是本篇文章的全部内容。如果你觉得对你有帮助的话,请三连支持博主哦!!你们的支持是我更新的动力。也欢迎各位大佬指出不足~~~谢谢!!