重点题目">
重点题目- 题型
- 链表指针的修改
- 经典题目
- 86. 分隔链表 (多练)">86. 分隔链表 (多练)
- 328. 奇偶链表">328. 奇偶链表
- 206. 反转链表">206. 反转链表
- 92. 反转链表 II(多练)">92. 反转链表 II(多练)
- 25. K 个一组翻转链表(多练)">25. K 个一组翻转链表(多练)
- 合并多个链表
- 删除节点
- 链表排序
- 48. 排序链表(多练,迭代)">48. 排序链表(多练,迭代)
- 147. 对链表进行插入排序">147. 对链表进行插入排序
- 708. 循环有序列表的插">708. 循环有序列表的插
- 头插法
- 尾插法
- 经典题目
- 链表节点的查询
- 环形链表
- 141. 环形链表">141. 环形链表
- 142. 环形链表 II(多练)">142. 环形链表 II(多练)
- 经典题型
- 160. 相交链表(Tricky)">160. 相交链表(Tricky)
- 19. 删除链表的倒数第 N 个结点">19. 删除链表的倒数第 N 个结点
- 876. 链表的中间结点 (多练)">876. 链表的中间结点 (多练)
- 环形链表
- 复合题型
- 链表指针的修改
- Tricky
- 138. 复制带随机指针的链表(多练)">138. 复制带随机指针的链表(多练)
- 146. LRU 缓存机制(多练)">146. LRU 缓存机制(多练)
- 460. LFU 缓存(没做)">460. LFU 缓存(没做)
- 2. 两数相加">2. 两数相加
- 445. 两数相加 II">445. 两数相加 II
https://leetcode-cn.com/problems/middle-of-the-linked-list/solution/kuai-man-zhi-zhen-zhu-yao-zai-yu-diao-shi-by-liwei/
重点题目
876. 链表的中间结点
- 这题很基础,一定要把算法细节理解好
两个考点
- 指针的修改
- 最典型的就是链表反转,反转链表 II。
- 链表的拼接
- 比如再比如合并有序链表等。
三个注意
- 出现了环,造成死循环。
- 分不清边界,导致边界条件出错。
搞不懂递归怎么做
- 可以看出,这两种写法不管是边界,入参,还是代码都不太一样。为什么会有这样的差异呢?
- 如果是前序遍历,那么你可以想象前面的链表都处理好了,怎么处理的不用管。相应地如果是后序遍历,那么你可以想象后面的链表都处理好了,怎么处理的不用管。
- 准确地说应该是前序遍历容易改成不需要栈的递归,而后续遍历需要借助栈来完成。
前序遍历
def dfs(head, pre):
if not head: return pre
next = head.next
# # 主逻辑(改变指针)在后面
head.next = pre
dfs(next, head)
后续遍历
def dfs(head):
if not head or not head.next: return head
res = dfs(head.next)
# 主逻辑(改变指针)在进入后面的节点的后面,也就是递归返回的过程会执行到
head.next.next = head
head.next = None
return res
技巧
DummyHead
- 将头节点变成中间节点,简化判断。
- 通过在合适的时候断开链接,返回链表的中间节点。
- 快慢指针
- 获取数组中间项和倒数第几项等特定元素
题型
链表指针的修改
经典题目
86. 分隔链表 (多练)
迭代自己写出来了,但是模模糊糊的。
答案
- 将链表分成两段
- 最后和起来
-
328. 奇偶链表
206. 反转链表
92. 反转链表 II(多练)
使用stack
-
25. K 个一组翻转链表(多练)
答案:
节点搜索+指针的修改
- 快慢指针分组
-
合并多个链表
21. 合并两个有序链表
迭代写法很简单。
递归写法 -
删除节点
237. 删除链表中的节点
public void deleteNode(ListNode node) { node.val = node.next.val; node.next = node.next.next; }
203. 移除链表元素
答案
使用DummyHead方便删除头结点
-
83. 删除排序链表中的重复元素
答案
-
82. 删除排序链表中的重复元素 II (多练)
答案
保留prev节点一定是没有重复的列表的尾端
通过curr 和 curr.next 判断重复元素
- 若是重复,找到第一个不重复的元素
- prev.next
链表排序
48. 排序链表(多练,迭代)
答案:
归并排序。快排不行,需要拿到左边的尾节点,所以不是nlogn
-
147. 对链表进行插入排序
答案:
使用dummyHead,方便在头节点之前插入节点
- 将头节点当作第一个sorted节点,然后他的下一个就是需要排序的节点
- 如果下一个节点,比以排序完的最后一个节点大,之间向后移一步
从前向后遍历插入位置,找到后就插入,不必向后移,因为我们把要插入的节点放到前面去了。
708. 循环有序列表的插
答案一:
先找到最小的节点
- 然后开始寻找插入为止,保留prev和curr节点,插入在中间。
答案二:
- do while 循环
- 退出条件为 节点又回到头节点了 此时搜完了所有可以插入的地方
- 保持一个变量,表示有无插入过。
-
头插法
尾插法
链表节点的查询
环形链表
141. 环形链表
142. 环形链表 II(多练)
快慢指针
Floyd 判圈算法
https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/huan-xing-lian-biao-ii-by-leetcode-solution/
答案 快慢指针环内相遇
-
经典题型
160. 相交链表(Tricky)
答案
走到尽头见不到你,于是走过你来时的路,等到相遇时才发现,你也走过我来时的路。
https://leetcode-cn.com/problems/intersection-of-two-linked-lists/solution/intersection-of-two-linked-lists-shuang-zhi-zhen-l/
19. 删除链表的倒数第 N 个结点
答案
-
876. 链表的中间结点 (多练)
答案
快慢指针
- 用两个指针 slow 与 fast 一起遍历链表。slow 一次走一步,fast 一次走两步。
- 将最后的NULL也当作一个节点,会有以下两种情况
- 偶数个节点:
- 算上最后的NULL节点会有2N次可以走
- 此时,快指针每次走两步,慢指针每次走一步,共N个迭代
- 终止条件为 fast == NULL 表示快指针到头了
- 此时slow走了N步,位于第N+1个节点,符合正确答案。
- 奇数个节点:
- 假设就比上个例子的偶数个节点多了一个节点
- Fast会走到下图5的位置,也就是之前NULL的位置。
- 这时候,slow移动了N个,到达了节点N+1,这已经是正确答案了
- 所以,我们fast要让停在这里,终止条件就是fast.next == null
- 偶数个节点:
- 结合以上的两种情况:我们一开始无法得知链表长度的奇偶性,但当我们遇到fast==null或者fast.next==null的时候就一定表示,slow到达正确答案的位置了。而且若fast==null表示链表长度为偶数,fast!=null表示链表长度为奇数。
- 总结如下图:
public ListNode middleNode(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
复合题型
234. 回文链表(多练)
答案
- 节点搜索+指针的修改
- 快慢指针走到中点。同时保留一个prev指针用于给慢指针反转链表。通过fast判断链表长度的奇偶性
- fast == null -> even
- fast != null -> odd
-
143. 重排链表 (多练)
先找到中点
- 反转中点后的链表
-
61. 旋转链表
答案:
-
Tricky
138. 复制带随机指针的链表(多练)
三步走
- 在原链表中,将副本放在左边。
- 通过原链表将副本的radom连起来
- 最后将副本抽出来再将next连起来
146. LRU 缓存机制(多练)
写了我将近两个小时。。。
答案:
伪头节点 + 伪尾节点 互相连起来
- 四个辅助函数
- moveToHead
- removeNode
- addToHead
- removeTail
460. LFU 缓存(没做)
2. 两数相加
-
445. 两数相加 II
用栈来reverse