首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

数据结构 线性结构篇——链表

一、前言

在前面两章我们讲解了动态数组、栈和队列的讲解,这些底层都是依托静态数组,靠resize解决固定容量问题的,之前虽然用户看到的是动态数组,但是依然使用的是静态数组,他是依靠resize这个方法解决 固定容量问题 ,但是我们今天要讲解的链表不一样,链表是我们数据结构学习的一个重点,也有可能是一个难点,为什么链表这么重要呢?因为他是最简单的也是真正的动态数据结构

二、为什么链表很重要

链表是一个真正的动态数据结构

最简单的动态数据结构

更深入的理解引用(或者指针)

更深入的理解递归

辅助组成其他数据结构

更深入的理解引用(或者指针):和内存相关,虽然在 java 中大家不用手动的管理内存,但是对链表这种数据结构,更加深入的理解,可以帮助大家对引用、指针、甚至计算机系统中和内存管理相关的很多话题,有更加深入的认识。

更深入的理解递归链表本来也是有他非常清晰的递归结构的,、由于链表这种数据结构是 数据结构,我们可以更加深入理解递归,对于递归这种深入理解是不可获取的。

链表本身也是具有功能性:辅助组成其他数据结构(hashMap 、栈和队列)

三、什么是链表

链表是一种数据结构,在内存中通过节点记录内存地址而相互链接形成一条链的储存方式。相比数组而言,链表在内存中不需要连续的区域,只需要每一个节点都能够 记录下一个节点 的 内存地址,通过引用进行查找,这样的特点也就造就了链表增删操作时间消耗很小,而查找遍历时间消耗很大的特点。

我们日常在Java中使用的LinkedList即为双向链表。而在链表是由其基本组成单元节点(Node)来实现的。我们在日常中见到的链表大部分都是单链表和双链表

单元节点 (Node)

e就是链表元素

next指的是当前节点的下一个节点

对于链表来说它就像我们的火车一样,每一个节点其实就是一节车厢,我们在车厢中存储真正的数据,而车厢和车厢之间还要进行连接,让我们数据是整合在一起的,用户可以方便的在所有的数据上进行查询或其他操作,那么数据和数据连接就是由这个next来完成的

当然链表不能无穷无尽,如果一个节点的nextNull了,就说明这个节点是最后一个节点了,这就是链表

如下图所示(单链表):链表的优点:真正的动态,不需要处理固定容量的问题链表的缺点:丧失了随机访问的能力

在数组中:每一个索引,直接从数组中拿出索引对应的元素,这是因为从底层机制上,数组所开辟的空间,在内存里是连续分布的,所以我们可以直接可以去找这个数组的偏移,直接计算出这个数据所存储的内存地址,可以直接使用。

链表:而链表是靠Next一层一层连接的,需要借助这个Next一点一点的去找我们需要取出来的元素。

四、创建我们自己的链表

4.1 链表基本结构

内部类Node:设计私有的内部类,对于用户来说不需要知道链表底层实现,不需要知道node这个节点,对用户屏蔽编码实现的底层实现e:元素next:指向Node的一个引用

4.2 添加元素

之前我们讲的是如何在数组中添加元素,我们在数组尾添加元素是非常方便的,因为对于数组来说是顺序排放的,有意思的是对于链表来说,恰恰相反,在链表头添加元素是非常方便的,其实这样非常好理解,对于数组来说我们有size这个变量,它直接指向了数组中最后一个元素下一个位置,也就是下一个待添加元素的位置,所以直接添加就非常容易,因为有size这个变量,在跟踪数组的尾巴,而对于链表来说我们设立了链表的一个头head,而没有变量来跟踪链表的尾巴,所以我们在链表头添加元素是非常方便的,最关键的就是node.next = head 和 head = node,如下图所示:

4.2.1 链表头添加元素

代码实现:

4.2.2 链表中间添加元素:

我们需要在索引为2的地方添加元素 ,我们只需要找到 要插入之前的节点(1),我们管它叫prev,然后把之前节点的(1) next指向 ,然后在将 的这个节点指向之前节点(1)之后的节点(2),就完成了整个插入了,其中关键代码就是 ,其中关键:我们要找到添加节点的前一个节点

代码实现:

4.2.3 添加操作时间复杂度4.3 为链表设计虚拟头结点

上面我们介绍了链表的添加操作,那么我们在添加的时候遇到了一个问题,就是在链表任意一个地方的时候,添加一个元素,在链表头添加一个元素,和在链表其他地方添加元素,逻辑上会有差别,为什么在链表头添加元素会比较特殊呢,因为我们在链表添加元素的过程,要找到待添加的之前的一个节点,但是由于对于链表头没有之前的一个节点,不过我们可以自己创建一个头结点,这个头节点就是虚拟头结点,这个节点对于用户来说是不存在, 用户也不会感知到这个节点的存在,我们是屏蔽了这个节点的存在,如下图所示:

代码实现:

4.4 链表元素 get、set、是否存在操作

4.5.1 修改和查找操作时间复杂度

4.5 删除链表元素

加入我们想要删除索引为(2)位置的元素,我们需要找到待删除节点之前的一个位置,也就是(1),我们用prev表示,找到这个节点之后,那么(2)就是我们需要删除的索引了 我们叫delNode,如下图所示:

代码实现:

4.5.1 删除操作时间复杂度4.6 完整代码

4.2.7 结果测试:

打印结果:

四、链表时间复杂度分析

对于增加和删除来说,如果是对链表头进行操作,那么就是O(1)级别的复杂度,对于查询来说,也是一样

五、链表应用

5.1 使用栈实现链表5.1.1 接口类:

5.1.2 实现类:

5.1.3 运行结果:

5.1.4 结果打印:

5.2 使用链表实现队列

5.2.1 接口类

5.2.2 实现类

5.2.2 测试类

打印结果:

六、更多链表结构

6.1 双链表

代码:

6.1 循环列表

代码:

在java中,LinkedList底层使用的就是循环链表,也就是循环双向链表,到这里我们链表就讲解完成了,在阅读中大家如果觉得有改进或者疑问的地方,欢迎大家在下面进行留言,博主看到了会第一时间回复大家。

我是牧小农,怕什么真理无穷,进一步有进一步的欢喜,大家加油!

--  End  --

———————

1.原创不易,你的在看是我创作的动力。

2.欢迎关注公众号 牧小码农,「带你一起学Java」

3.疫情期间,勤洗手,戴口罩,做好个人防护。

“在看转发”是最大的支持

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20210107A03H8700?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券