代码随想录二刷 |链表 | 链表总结
- 理论基础
- 分类
- 单链表
- 双链表
- 环形链表
- 存储方式
- 链表的定义
- 链表操作
- 删除链表节点
- 添加链表节点
- 性能分析
- 移除链表元素
- 在原链表上移除
- 虚拟头节点移除
- 设计链表
- 反转链表
- 双指针
- 递归
- 两两交换链表节点
- 移除链表的倒数第 N 个节点
- 链表相交
- 环形链表II
理论基础
每一个节点都有两部分组成,一个是数据域(存放节点的数据)一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向NULL,入口处的节点又称空指针。
分类
单链表
每一个节点都有两部分组成,一个是数据域(存放节点的数据)一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向NULL,入口处的节点又称空指针。
双链表
一个节点有两个指针域,分别指向前一个节点和后一个节点,它可以双向查询。
环形链表
头节点和尾节点相连
存储方式
链表的节点在内存中是散乱分布在内存的某地址上,分配机制取决于操作系统的内存管理
链表的定义
结构体中至少需要包括数据,指向下一个节点的指针以及节点的构造函数。
如果不定义函数,不能在定义链表时初始化
struct ListNode{int val;ListNode* next;ListNode(int x) : val(x), next(nullptr) {}
};
链表操作
删除链表节点
前一节点跳过该节点指向下一节点
添加链表节点
前一节点指向该节点,该节点指向下一节点
性能分析
插入/删除(时间复杂度) | 查询(时间复杂度) | 适用场景 | |
---|---|---|---|
数组 | O(n) | O(1) | 数据量固定,频繁查询,较少增删 |
链表 | O(1) | O(n) | 数据量不固定,较少查询,频繁增删 |
移除链表元素
在原链表上移除
- 移除头节点:将头节点设置为后一个节点,并且回收头节点
- 其他节点:将前一个节点的指针指向下一个节点,并且回收该节点
虚拟头节点移除
设置虚拟头节点,从虚拟头节点开始遍历,删除每一个节点都是使用前一个节点的指针指向下一个节点,并且删除当前节点,当遍历结束,返回虚拟头节点的后一个节点
设计链表
- 设置虚拟头节点,方便统一操作头节点
- 获取链表的第几个元素,此时应该取到正对的那个节点,因此cur应该设置为
head
而不是dummyHead
,并且由于index
从0开始,因此index不能大于size() - 1
- 在指定
index
前插入和删除,需要都遍历到目标节点的前一个节点,因此cur
应当从dummyHead
开始 - 插入一个节点
index
可以是size
,因为实际会取到左后一个节点并且在最后一个节点后边插入,删除节点index
不能是size
,因为没有size
这个节点
反转链表
双指针
定义一个cur指针指向头节点,再定一个pre指针初始化为NULL。首先要把cur->next节点用temp指针保存一下,也就是保存一下这个节点,将cur->next指向pre,此时已经反转了一个节点,然后pre = cur,cur = pre
递归
- 从前向后:类似于双指针只是把pre = cur,cur = pre这个部分用递归实现
- 从后向前:先遍历到最后,然后利用cur -> next -> next = cur进行翻转
两两交换链表节点
dummyHead指向头节点,cur指针指向dummyHead,再定义两个指针用于保存节点:
tmp = cur -> next;
tmp1 = cur -> next -> next -> next
- 第一步:cur -> next = cur -> next -> next
- 第二步:cur -> next -> next = tmp;
- 第三步:cur -> next -> next -> next = tmp1;
交换完两个以后将cur向后移动两位
移除链表的倒数第 N 个节点
使用双指针,一个指针先走N步,然后两个指针一起向后遍历,当前边的指针先到达尾节点,后边的指针位于第N节点,之后将该节点的 -> next -> next赋给next,完成对节点的删除
前边的指针先走N步后,还应该再走一步,保证后边的指针实际是在第N个节点的前一个,从而方便删除
链表相交
- 本题要求的实际是两个链表首个交点的指针,交点比较的是指针相同而不是数值相等
- 将两个链表结尾靠在一起,然后一个指针指向短链表的开始,一个指向长链表与短链表开头对齐的地方
- 两个指针一起向后遍历,直到找到两个指针相同的节点,返回该节点即可
环形链表II
需要解决两个问题:
- 如何判断链表有环
- 如果有环,如何找到环状入口节点
可以使用快慢指针法,分别定义fast和slow指针,从头节点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果fast和slow指针在途中相遇,说明这个链表有环
相遇时慢指针走过了x + y,快指针走过了x + y + n * (y + z),因为快指针一步走两个节点,是慢指针的两倍,假设快指针只多走了一圈,那么 n = 1,从而 x = y,也就是一个指针从头走,一个从交点走,两个指针第一次相遇的位置即为环形入口节点。
本文参考录友@海螺人的总结思维导图