Python微信订餐小程序课程视频
https://edu.csdn.net/course/detail/36074
Python实战量化交易理财系统
https://edu.csdn.net/course/detail/35475
简介
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CiSlM7zl-1644617304947)(https://image.fatedeity.cn/markdown/2022-02-10-120107.png “链表结构”)]
链表是一种和数组不同的线性表结构,数组的存储使用了一组连续的内存空间,而链表通过链接的方式将零散的内存空间串联起来使用。
链表的定义需要注意两点:链表仍然是一个线性表结构;链表不使用连续的内存空间进行存储。
因此,链表克服了数组需要预先知道数据大小的缺点,并且能充分利用计算机内存空间,实现灵活的内存动态管理。
但链表也失去了快速随机存取的优点,同时由于增加了结点的指针域,空间开销较大。
基本概念
链表是通过指针将零散的内存块串联在一起的,这里的内存块被称为链表的结点。
结点除了存储数据之外,还会存储下一个结点的地址,这个记录下一个结点地址的指针被称为后继指针。在双向链表中,还会存储上一个结点的地址,这个记录上一个结点地址的指针被称为前驱指针。
链表中存在两个比较特殊的结点,分别是第一个结点和最后一个结点。因此也给这两个结点取了名称,第一个结点被称为头结点,最后一个结点被称为尾结点。
随机存储
把链表和数组作比较,数组具有快速随机存取的优点,而链表是随机存取效率非常低的数据结构。
如果通过下标去访问链表中的结点,是不能使用寻址公式的,只能通过头结点作为入口,根据指针一个结点一个结点地依次遍历,直到找到对应的结点。
综合计算下来,链表做随机访问的时间复杂度为 O(n)O(n)O(n),效率比数组低得多。
插入、删除
虽然链表没有了数组随机存取的优点,但在插入、删除结点的时候,效率比数组高很多。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hhR522C6-1644617304952)(https://image.fatedeity.cn/markdown/2022-02-10-120149.png “链表的插入和删除”)]
假设,要在链表中插入一个结点,在知道插入位置的前后两个相邻结点的前提下,只需将新结点的后继指针指向下一个结点,然后将上一个结点的后继指针指向这个新结点,即可完成插入结点的操作。删除结点也是类似的操作,非常方便。
但是,插入、删除结点时效率高的前提是知道操作位置的相邻结点,否则仍需要从头结点开始寻找到对应位置,这样的效率会非常低。
五花八门的链表
链表有很多不同的类型,在上面说的都是最简单的单向链表,复杂一点的还有双向链表、循环链表。
单向链表
单向链表是最简单的链表结构,它包含两个域,一个信息域和一个指针域。
信息域存储实际的数据,指针域存储此结点的下一个结点位置。
在实际编码中,为了方便,头结点的信息域是空的,其指针域存储实际的第一个结点所在位置,尾结点的指针域一般会是 NULL
地址。
双向链表
双向链表是在单向链表的基础上多增加了一个指针域,这个新增加的指针域会存储上一个结点所在位置。
也就是说,在双向链表中,除头结点的任意结点都可以访问到上一个结点,因此称为双向链表。
循环链表
循环链表是一个头结点和尾结点连接在一起的特殊链表,通过单向链表或双向链表都能够实现。
循环链表的优点就是从链表的尾结点到头结点非常方便,也方便处理具有环形结构特点的数据,如约瑟夫问题。
使用上的问题
数组和链表如何选择
仅特性和效率而言,数组拥有快速随机访问的特性,链表可以快速插入、删除。经常利用下标访问元素可以使用数组,经常插入、删除元素可以使用链表。
但是,不能仅仅只用复杂度分析决定使用哪种数据结构。数组简单易用,而且能够借助 CPU 缓存机制预读数组中的数据;而链表在内存中不是连续存储的,对 CPU 缓存不友好,没办法有效预读。
数组的缺点是数据大小固定,而且一经声明要占用整块连续内存空间,如果声明的数组过大,系统可能没有足够的连续内存空间分配给它。即使是在 Java 中使用可以动态扩容的 ArrayList 类型,也存在扩容耗时的问题。而链表本身没有大小的限制,天生支持动态扩容。
实现链表的技巧
编写一个正确的链表是比较难的,但是其中也有一些技巧:
-
理解指针或引用的含义。将某个变量赋值给指针,实际上就是将这个变量的地址赋值给指针,或者反过来说,指针中存储了这个变量的内存地址,这个内存地址存储这个变量,通过指针能找到这个变量
-
警惕指针丢失。插入结点时,先将插入结点的后继指针指向下一个结点,再把前一个结点的指针指向插入结点,这样才不会丢失指针
-
避免内存泄漏。删除结点时,要记得手动释放内存空间
-
利用哨兵简化实现难度。在实际开发当中,如果向空链表中插入第一个结点的时候,还需要判断链表中是否已经存在头结点,嵌入代码比较严重;但是,如果增加一个哨兵结点,哨兵结点的后继指针指向头结点,则可以省略这一步操作
-
重点留意边界条件处理。当链表为空的时候,代码是否能正常工作?当链表只有一个结点的时候,代码是否能正常工作等等
-
举例画图,辅助思考。链表的指针指向会比较复杂,这种情况可以通过举例画图的办法将各种情况列举出来,这样思路会更加清晰
-
多写多练,熟能生巧。写链表代码是非常考验逻辑思维能力的,多多尝试练习可以提高逻辑思维能力
-
__EOF__
fatedeity - 本文链接: https://blog.csdn.net/fatedeity/p/15879484.html
- 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
- 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角**【[推荐](javascript:void(0)😉】**一下。