算法笔记(三):链表

109 阅读5分钟

链表: 一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储其它结点地址的指针域。 相比于数组结构,操作稍复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间。

哨兵: 一个哑对象,假设在链表L中设置一个对象A,该对象没有值,但也具有和其他对象相同的各个属性。对于链表代码中出现的每一处对A的引用,都代之以对哨兵L.A的引用。其作用是简化边界条件的处理。例如,删除单链表的第一个结点,因单链表的删除操作需要找到该结点的前置结点,让其指向下一个结点,这个时候第一个结点是没有前置结点的,处理起来就很麻烦,而通过在头部添加一个哨兵就能很好的解决这种情况(尾部添加也能达到效果),双链表则往往需要在头尾各加一个哨兵

这里我把链表分成单链表,双链表,环形链表,相交链表进行记录练习(文中算法题来自LeetCode,解题代码来自作者本人,考虑到swift的普及性,本章开始算法代码开始使用Python编写)

单链表: 指针域存在一个next指针指向下一个结点.最后一个结点的next指针一般指向哨兵

例题: 给定一个已排序的链表的头head,删除原始链表中所有重复数字的节点,只留下不同的数字。返回已排序的链表。(原始链表中的值大于-101小于101) 例如: 输入:head = [1,2,3,3,4,4,5] 输出:[1,2,5]

解:为了方便删除第一个元素,可以现在头部添加一个哑对象,数值取101避免重复,然后使用快慢指针标记重复的区域并删除

截屏2022-09-09 下午2.54.12.png

双链表: 指针域存在一个next指针指向下一个结点,同时还有一个prev指针指向上一个结点,第一个结点的prev指针与最后一个结点的next指针一般指向哨兵

例题: 你会得到一个双链表,其中包含的节点有一个下一个指针、一个前一个指针和一个额外的 子指针 。这个子指针可能指向一个单独的双向链表,也包含这些特殊的节点。这些子列表可以有一个或多个自己的子列表,以此类推,以生成如下面的示例所示的 多层数据结构 。

截屏2022-09-09 下午3.12.15.png

给定链表的头节点head,将链表扁平化,以便所有节点都出现在单层双链表中。让curr是一个带有子列表的节点。子列表中的节点应该出现在扁平化列表中的curr之后和curr.next之前。返回扁平列表的head。列表中的节点必须将其所有子指针设置为null。

解: 可以采用递归的方式从头部开始遍历链表,遇到子列表时进行递归,然后继续向下遍历

截屏2022-09-09 下午3.07.31.png

环形链表: 单链表中最后一个结点的next指针指向头结点,双链表中最后一个结点的next指针指向头结点,同时头结点的prev指针指向最后一个结点.这样就形成了一个环形链表

例题: 给定一个链表的头节点head,返回链表开始入环的第一个节点。如果链表无环,则返回null。如果链表中有某个节点,可以通过连续跟踪next指针再次到达,则链表中存在环。为了表示给定链表中的环,评测系统内部使用整数pos来表示链表尾连接到链表中的位置(索引从0开始)。如果pos是-1,则在该链表中没有环。注意:pos不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改链表。

截屏2022-09-09 下午3.31.30.png

解: 可以使用快慢两个指针,快指针每次走两步,慢指针每次走一步,当第一次相遇时,假设x为head到相交结点的距离,y为相连结点到相遇结点的距离,z为环形链表的距离,n为快指针走过的环形链表圈数,则有:2(x+y)=x+nz+y,等式左边是慢指针的路程,由于速度是快指针的二分之一,所以乘二,等式右边为快指针的路程,简化可得x = nz - y.这样让其中一个指针回到头部,两个指针以同样的速度运行,再次相遇时就是相连结点的位置.

截屏2022-09-09 下午3.29.30.png

相交链表: 两条单链表从某一个结点开始重合,该结点不是第一个结点

例题: 给你两个单链表的头节点headA和headB,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回null。如图:

截屏2022-09-10 17.16.34.png

解: 当把链表尾部与其中一个头部相连时,就变成了环形链表求相连结点的问题

截屏2022-09-09 下午3.35.07.png

补充: 数组查询快,链表插入删除快,各有优势,但是实际开发中,数组的效率是远大于链表的,由于数组使用的是CPU一级缓存,而链表一般使用的是内存,不同的CPU时钟周期会造成数十倍的速度差.机器不同会有差异,一般速度差距在30倍左右

算法笔记目录

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。