LeetCode 203. 移除链表元素
题目描述
给你一个链表的头节点 head 和一个整数 val,
请你删除链表中所有节点值等于 val 的节点,并返回新的头节点。
示例
输入: head = [1,2,6,3,4,5,6], val = 6
输出: [1,2,3,4,5]
思路讲解
链表操作的常见套路是「虚拟头节点 dummy + 遍历指针」。
因为:
-
删除节点时,如果被删的是第一个节点(头节点) ,处理会比较麻烦;
-
所以我们可以在链表前加一个虚拟头节点 dummy,它的
next = head; -
然后用一个指针
cur从dummy开始遍历:- 每次检查
cur.next; - 如果
cur.next.val === val,就删除它:cur.next = cur.next.next; - 否则
cur = cur.next。
- 每次检查
最后返回 dummy.next(新的头结点)。
动态过程演示
例子:
head = 1 → 2 → 6 → 3 → 4 → 5 → 6
val = 6
过程(加虚拟头 dummy):
dummy → 1 → 2 → 6 → 3 → 4 → 5 → 6
↑
cur
cur.next.val = 1 ≠ 6 → cur = cur.next
cur.next.val = 2 ≠ 6 → cur = cur.next
cur.next.val = 6 = 6 → 删除该节点
链表变为:dummy → 1 → 2 → 3 → 4 → 5 → 6
继续往后直到结尾。
JavaScript 实现
function removeElements(head, val) {
// 创建虚拟头节点
let dummy = new ListNode(0);
dummy.next = head;
let cur = dummy;
while (cur.next) {
if (cur.next.val === val) {
cur.next = cur.next.next; // 删除节点
} else {
cur = cur.next; // 移动指针
}
}
return dummy.next;
}
时间与空间复杂度
| 项目 | 复杂度 |
|---|---|
| 时间 | O(n)(遍历整个链表一次) |
| 空间 | O(1)(只用了常数级额外空间) |
LeetCode 206. 反转链表
思路(迭代法:三个指针)
反转链表其实就是让所有指针“掉头”:
例如原链表:
1 → 2 → 3 → 4 → null
我们要得到:
null ← 1 ← 2 ← 3 ← 4
核心想法
我们用三个指针:
prev:当前节点的前一个节点(初始为null)curr:当前正在处理的节点(从head开始)next:暂存curr.next(因为指针反转后会丢失)
每次循环:
- 暂存下一个节点
next = curr.next - 反转指针方向:
curr.next = prev - 前进一步:
prev = curr - 继续下一个:
curr = next
直到 curr == null,prev 就是新链表头。
图解过程
初始状态:
null ← prev curr → 1 → 2 → 3 → 4 → null
第一步:
next = 2
curr.next = prev => 1 → null
prev = 1
curr = 2
第二步:
next = 3
curr.next = prev => 2 → 1 → null
prev = 2
curr = 3
...
最终:
4 → 3 → 2 → 1 → null
返回 prev(即 4)
代码(JavaScript)
var reverseList = function(head) {
let prev = null;
let curr = head;
while (curr) {
let next = curr.next; // 暂存下一个节点
curr.next = prev; // 反转指针方向
prev = curr; // 前进
curr = next; // 继续
}
return prev; // 新头节点
};
示例
输入:
1 → 2 → 3 → 4 → 5 → null
输出:
5 → 4 → 3 → 2 → 1 → null
拓展:递归写法(更优雅)
递归思路:反转后半部分,让 head.next 指向自己。
var reverseList = function(head) {
if (!head || !head.next) return head;
let newHead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
};
思维图:
反转(1→2→3)
=>
反转(2→3)
=>
反转(3) → 返回 3
然后在回溯时:
2.next.next = 2
2.next = null
总结对比
| 方法 | 思路 | 空间 | 优点 |
|---|---|---|---|
| 迭代法 | 三指针依次翻转 | O(1) | 简洁高效 |
| 递归法 | 从尾到头回溯反转 | O(n) | 更优雅但栈消耗大 |