单链表专题

目录

创作不易,如有帮助,还望三连,谢谢!!!

1.链表的概念及其结构

2.单链表的实现

2.1-1单链表内容打印

2.1-2链表结点生成

2.1-3尾插

2.1-4头插

2.1-5尾删

2.1-6头删

2.1-7查找链表数据

2.1-8指定位置前插入数据

2.1-9删除指定位置节点

3.链表的分类


创作不易,如有帮助,还望三连,谢谢!!!

1.链表的概念及其结构

之前我们学习了顺序表,学习了动态顺序表的功能的实现,那么今天我们就来学习一下线性表中的单链表。

那么,什么是链表呢?

概念:链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

链表由一个个节点构成,而通过一个节点可以找到另一个节点(通过指针来实现),就比如单链表,它的每个节点中有一个next指针指向下一个节点,所以说它在物理存储结构上是非连续的。

我们之前学过的顺序表在物理存储结构上是连续的(即在内存中是连续存放的),然而链表在逻辑结构上是连续的,因为单链表中的元素是按顺序排列的,并且每个节点都有一个指针指向下一个节点,从而形成了一个逻辑上的连续序列。

这就好比火车一样:

火车的每个车厢都是独立存在的,链表中的节点也是独立存在的;每门车厢都有车门,那么想要打开车门进入下一个车厢,就需要一把钥匙,而链表节点中的指针就是这把“钥匙”。

在链表⾥,每节“⻋厢”是什么样的呢?

与顺序表不同的是,链表⾥的每节"⻋厢"都是独⽴申请下来的空间,我们称之为“结点/节点”
节点的组成主要有两个部分:。当前节点要保存的数据和保存下⼀个节点的地址(指针变量)
图中指针变量plist保存的是第⼀个节点的地址,我们称plist此时“指向”第⼀个节点,如果我们希
望plist“指向”第⼆个节点时,只需要修改plist保存的内容为0x0012FFA0。

那么为什么还需要指针变量来保存下⼀个节点的位置?

我们上面讲了链表在物理存储结构上不一定是连续的,所以说要找到下一个节点不像数组那样通过下标就能找到,所以说我们要通过指针变量来保存下⼀个节点位置才能从当前节点找到下⼀个节点。

单链表每个节点的结构体代码如下:

与顺序表结构体基本相似,除了命名不同和多了一个指向下一个结点的指针外,基本相同,不再赘述。

2.单链表的实现

与顺序表一样,我们最好建立三个文件,分别是SList.h头文件,SList.c和test.c源文件。

那么我们要实现哪些功能呢?如下图所示:

仔细一看会发现跟顺序表基本一样。那接下来我们就来一个一个实现。

2.1-1单链表内容打印

内容打印很简单,只需创建一个指针cur遍历链表节点,打印数据内容即可,不在多讲。

2.1-2链表结点生成

我们学习知识要学会去类比,我们之前学习了顺序表,知道了顺序表的扩容用的是realloc函数,而链表节点生成可以类比于顺序表的扩容,所以大家想一个问题:单聊表结点生成用哪一个函数呢?也是realloc吗?

答案:用malloc函数。

为什么?因为我们上面讲了链表中的结点在物理结构上是不一定连续的,每个节点都是独立的,所以应该用malloc函数独立开辟一块块的空间。

代码如下:

也没什么别的特别需要注意的地方。

2.1-3尾插

我们先来分析一下思路:

尾插数据,无非要先生成一个新的结点,然后进行尾插;想要尾插,我们要找到最后一个结点,然后在插入新节点。

示意图如下:

那么大家看我下面这段代码有没有问题:

细心的小伙伴肯定能发现:如果链表是空链表呢?

确实,再回头看看代码以及思路,我们漏了链表为空的情况,链表为空,即phead指针指向NULL,这种情况也比较简单,我们只需让phead指向新的节点即可,代码如下:

那么现在代码还有错误吗?

我们运行一下,看看效果:

我们发现代码没有正常运行,崩溃了!为什么呢?

当程序员就是要写出各种BUG然后再独自处理掉这些BUG,从而变得更加强大。

其实,仔细想想,我们就能发现,问题出现在这里:

看出来问题了吗?我们之前学过传值调用和传址调用,这里实参是个指针,指向链表头结点,而形参是个一级指针,这不相当于传值调用吗?此时,该phead指针只是实参plist的一份临时拷贝,它俩在内存中是独立存储的,改变phead不会影响到plist。

所以,为了解决这个问题,我们应该传址调用,即传地址,用二级指针接收一级指针plist的地址,通过指针找到plist,进而来修改plist的指向。

修改后的代码如下图所示:

所以我们后面进行头尾插入,删除形参都要用二级指针。

2.1-4头插

思路示意图如下:

那么,有的小伙伴要问了:第一步和第二步能不能互换一下顺序?

答案:不能。

如果先改变plist的指向,让它指向newnode,那么你怎么再去寻找plist原来指向的节点呢?

所以顺序不能互换,代码如下:

2.1-5尾删

思路:首先得分情况,如果链表只有一个节点,直接释放掉该节点;如果有2个或两个以上节点,就用两个指针:prev和pcur找到最后一个节点和倒数第二个节点,然后进行修改即可。

思路示意图如下:

最终代码如下图所示:

2.1-6头删

相比于尾删,头删代码更加简单,只需记录要删除的位置,改变*pphead指针的指向即可,不在赘述。

2.1-7查找链表数据

比较容易理解,不再赘述。

2.1-8指定位置前插入数据

思路:找到pos和pos之前一个节点,然后进行修改插入。

思路示意图:

同时要考虑特殊情况:*pphead=pos;这种情况就是头插操作,我们上面讲过,不再赘述。

代码如下:

2.1-9删除指定位置节点

思路:找到pos前后两个节点,然后进行删除,修改。

同时也要注意特殊情况:即头删情况。

代码如下:

之后几个函数与我们讲的都大同小异,就不在讲解了。

3.链表的分类

链表的结构⾮常多样,以下情况组合起来就有8种(2*2*2)链表结构:

我们对此了解一下即可。

虽然有这么多的链表的结构,但是我们实际中最常⽤还是两种结构:单链表和双向带头循环链表。

我们上面讲解的就是无头单向非循环链表。

1.⽆头单向⾮循环链表:结构简单,⼀般不会单独⽤来存数据。实际中更多是作为其他数据结构的⼦结构,如哈希桶、图的邻接表等等。另外这种结构在笔试⾯试中出现很多。
2.带头双向循环链表:结构最复杂,⼀般⽤在单独存储数据。实际中使⽤的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使⽤代码实现以后会发现结构会带?来很多优势,实现反⽽简单了,后⾯我们代码实现了就知道了。

  • 75
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 31
    评论
评论 31
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值