链表的相关知识
1.1链表的概念:
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表
中的指针链接次序实现的 。
1.2 链表的优缺点
链表的优点:
由于链表上的元素在空间存储上内存地址不连续所以随机增删元素的时候不会有大量元素位移,因此随机增删效率较高。
在以后的开发中,如果遇到随机增删集合中元素的业务比较多时,建议使用LinkedList。
链表的缺点:
不能通过数学表达式计算被查找元素的内存地址,每一次查找都是从头
节点开始遍历,直到找到为止。所以LinkedList集合检索/查找的效率
较低。
2.链表的分类
2.1单向和双向:
![](https://img-blog.csdnimg.cn/img_convert/71537c71d603b5142852afc7b62b29bc.jpeg)
2.2带头和不带头:
![](https://img-blog.csdnimg.cn/img_convert/2e51f13e7356ac032c1af75d746d89dd.png)
2.3不循环链表和循环链表
![](https://img-blog.csdnimg.cn/img_convert/f6c6d2f283e4458ea99e40b677aad868.png)
链表有很多种类别,但我们常用就只有无头单向非循环链表和带头双向循环链表
1.无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结
构的子结构,如哈希桶、图的邻接表等等。
2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带
来很多优势,实现反而简单了。
3.单链表的实现
今天讲无头单向非循环链表的实现,后续会补充带头双向循环链表
下图链表的基本接口
![](https://img-blog.csdnimg.cn/img_convert/f10348bc951eaba54635412dbc740c69.png)
这里可以发现多个接口都用了二级指针,为什么这里会用到二级指针呢,
原因很简单,因为尾插,尾删,头插,头删都有可能改变头指针指向的位置(即头指针plist存放的内容)
3.1 单链表的实现
链表是由多个节点相互连接形成的一种数据结构,总的来说链表的基本单位是节点
3.2第一步需要先创建一个节点(结构体)
![](https://img-blog.csdnimg.cn/img_convert/7dff7f24c066869df913914aed380753.png)
其成员有两个:
其一 SLDataType类型的结构体变量data用来存放数据
其二next指针用来存放下一个节点的地址,以便节点间连接
现在来说说链表是怎么相连接起来的吧,链表中每个节点都有一个next指针用来存放下一个节点的地址,这样就可以把每个节点连接起来了
![](https://img-blog.csdnimg.cn/img_convert/f3f612726413b379093bb0fd5569cdd7.png)
上表相当于链表的示意图,方便我们了解,实际上它们之间是没有箭头相连的
对于一个空链表内部是不会有任何数据的,所以我们用来指向链表的头指针plist此时是指向NULL的
3.3节点的申请
申请一个节点,然后给data赋值,最后返回这个节点的地址
![](https://img-blog.csdnimg.cn/img_convert/b209f428f902112e269d69ad2521cfcc.png)
3.4链表的尾插
申请玩节点以后就可以进行数据的插入了
尾插就是在链表的最后一个节点后面插入一个节点,考虑到如果此时链表为空的话,则直接让头指针指向该节点,不为空那么就找链表的尾(最后一个节点)让其指向新的节点即可
![](https://img-blog.csdnimg.cn/img_convert/6a0dd69c188b7db9075df470afd81b47.png)
3.5链表的尾删
在删除一个数据的时候都会判断1.该链表为不为空,就像在顺序表中删除一个数据一样,如果为空就没有数据可以删了,所以报出该错误。
2.当链表只有一个元素的时候,且该元素被删除了,需要把头指针plist置空
3.以上两种情况都没有的话,直接找链表的尾,此时需要保存倒数第二个节点的地址,所以用到了pvr指针,当把最后一个节点删除后,此时pvr指针就为指向最后一个节点的指针了,让pvr->next==NULL
![](https://img-blog.csdnimg.cn/img_convert/e34950294dbdbdda11912dd077504ea0.png)
3.6链表的打印
这就是遍历一遍链表,打印出存进去的内容
![](https://img-blog.csdnimg.cn/img_convert/932f9f3f82831847dc05d0e01836b5b4.png)
3.7检查接口的准确性
![](https://img-blog.csdnimg.cn/img_convert/26b4f2acaed82db1482762402e8283ca.png)
由上图发现目前实现的接口没有问题
3.8链表的头插
这里并不需要判断链表是否为空,因为就算链表为空也没有影响
![](https://img-blog.csdnimg.cn/img_convert/0320e1f699a722e0c3445b4c2d884691.png)
3.9链表的头删
删除判断链表是否为空应该是不用多说了的
这个相对与尾删是比较简单的,首先让一个指针保存头节点的地址,然后让头指针指向第二的节点
最后释放掉第一个节点就ok了
![](https://img-blog.csdnimg.cn/img_convert/bde00da72910dd47c19597395e708c96.png)
4.0数据的查找
传入需要查找的值X,如果找到了就返回该值所在节点的地址,如果找不到就返回NULL;
这也是比较简单的,就直接上代码了,不过多说了
![](https://img-blog.csdnimg.cn/img_convert/b1957a728076419d175bd3c3562eadca.png)
4.1任意位置的插入(指定位置的插入)
在指定位置插入一个数据的时候肯定是不许pos为空的,
当*pphead(plist)头指针的值和pos相等的时候就相当于头插,直接调用头插函数就行
其他情况,找到pos节点前面的一个节点的地址存在prev中,然后让新节点连接pos节点,最后让prev节点连接新节点
![](https://img-blog.csdnimg.cn/img_convert/c8977e72dc6600da064570e3a2311bb4.png)
4.2任意位置的删除(指定位置删除)
当需要删除一个数据的时候判断链表是否为空(*pphead==NULL)就不多说了,上面已经提过了
当pos等于*pphead时,此时就相当于头删,头删的原理上文也已经阐述过了,就不过多解释了
其他情况,找到pos节点前面的一个节点的地址存在prev中,然后让prev节点连接pos后面的节点,最后把pos这个节点释放掉
![](https://img-blog.csdnimg.cn/img_convert/bb2e70b677b62909d328a5cbd61801d7.png)
4.3任意位置后面插入数据(指定位置后面插入数据)
这不会改变头指针,所以用一级指针就OK了
![](https://img-blog.csdnimg.cn/img_convert/aa0fdc8c1c2c199930fd958de4bf8ccd.png)
4.4删除任意位置后面的数据(删除指定位置后面的数据)
![](https://img-blog.csdnimg.cn/img_convert/9071db830ffc3ce87bd4bd07c3d787bb.png)
4.5链表的销毁
![](https://img-blog.csdnimg.cn/img_convert/07ddc4ab8e2dd54b32bbb12e07641564.png)
最后面这三个接口比较简单不再过多阐述
单链表就介绍到这了,如果有错误的地方还望能指出,以便我们共同进步
最后在不同文件中定义接口的时候可不要忘了声明这个接口的函数哈,
今天的分享就到这吧(886!!!),路过的小伙伴们给个关注吧,感谢支持