1.第 5 天 链表基础结构与操作 203. 移除链表元素 题目建议: 本题最关键是要理解 虚拟头结点的使用技巧,这个对链表题目很重要。 题目链接:https://leetcode.cn/problems/remove-linked-list-elements/ 视频讲解:https://www.bilibili.com/video/BV18B4y1s7R9
2.解题思路
虚拟头节点迭代法
(1) 引入虚拟头节点 dummy :创建一个值为0的虚拟节点,让它的 next 指向原链表的头节点。这样一来,无论原头节点是否需要删除,我们都可以用统一的逻辑处理,避免单独写代码处理头节点的特殊情况。
(2) 遍历链表:用一个指针 cur 从 dummy 开始遍历,检查 cur->next 的值:
- 如果 cur->next->val == val ,说明 cur->next 是要删除的节点,直接让 cur->next = cur->next->next ,跳过该节点;
- 如果不相等,就将 cur 移动到 cur->next ,继续遍历。
(3) 返回结果:遍历结束后, dummy->next 就是新链表的头节点。
3.遇到的问题
(1) 头节点处理不当
一开始直接从 head 开始遍历,遇到 head->val == val 时,不知道如何更新头节点,导致代码逻辑混乱。虚拟头节点就是解决这个问题的最佳方案。
(2) 删除节点后指针移动错误
删除 cur 节点后, cur 直接移动到 cur->next ,但此时 cur 已经是被删除的节点,会导致遍历出错。正确的做法是:删除节点时不移动 cur ,不删除时才移动。
4.代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* cur = dummyHead;
while (cur->next != NULL) {
if(cur->next->val == val) {
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
} else {
cur = cur->next;
}
}
head = dummyHead->next;
delete dummyHead;
return head;
}
};
5.收获
1. 虚拟头节点的技巧
这是链表题中非常实用的技巧。当需要频繁修改头节点时,引入虚拟头节点可以统一处理逻辑,避免单独判断头节点,让代码更简洁、更健壮。
2. 链表删除的核心逻辑
删除节点的本质是修改前驱节点的 next 指针,让它直接指向目标节点的后继节点,从而跳过目标节点。操作时必须保证:
- 持有目标节点的前驱节点指针;
- 操作后链表不会断裂。