题目来源
203. 移除链表元素 - 力扣(LeetCode)
题目描述
给你一个链表的头节点
head
和一个整数val
,请你删除链表中所有满足Node.val == val
的节点,并返回 新的头节点 。示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]示例 2:
输入:head = [], val = 1 输出:[]示例 3:
输入:head = [7,7,7,7], val = 7 输出:[]提示:
- 列表中的节点数目在范围
[0, 104]
内1 <= Node.val <= 50
0 <= val <= 50
题目限制
题目用最优解实现求解
思路分析
一、整体思路概述
解决这个问题的核心思路是遍历整个链表,在遍历过程中判断每个节点的值是否等于给定的 val
,如果相等,则将该节点从链表中删除,最终返回处理后的链表头节点。不过,在实际操作过程中,为了便于统一处理逻辑,尤其是应对要删除的节点可能是链表头节点这种特殊情况,我们通常会采用添加 “虚拟头节点” 的技巧来简化代码实现。
二、具体步骤与思路解析
(一)处理边界情况(空链表)
首先需要考虑一种特殊的边界情况,那就是当传入的链表本身就是空链表时(即 head
为 nullptr
),这种情况下没有节点可供删除操作,直接返回原链表的头节点 head
就可以了,因为空链表无需做任何修改,保持原样返回就是正确的处理方式。
(二)添加虚拟头节点
为了让链表的删除操作逻辑更加简洁统一,我们创建一个新的节点作为虚拟头节点,例如:ListNode* dummy = new ListNode(-1);
(这里给虚拟头节点赋的值 -1
只是一个示例,具体值不影响操作逻辑,只要便于区分就行),然后让这个虚拟头节点的 next
指针指向原来链表的头节点,即 dummy->next = head;
。
此时,我们后续的操作就可以从这个虚拟头节点开始遍历链表了,这么做的好处在于,当需要删除的节点恰好是原链表的头节点时,我们无需单独写额外的逻辑去处理头节点的删除情况,而是可以和删除其他普通节点一样,按照统一的逻辑进行处理,大大简化了代码的复杂度和逻辑判断。
(三)遍历链表并删除指定值的节点
接下来就是通过循环来遍历整个链表,以实现对每个节点值的检查以及相应的删除操作。常用的做法是使用一个指针(设为 cur
),初始时让它指向我们创建的虚拟头节点,然后通过 while(cur->next)
这样的循环条件来进行循环,只要 cur
指针指向的当前节点的下一个节点存在(即 cur->next
不为 nullptr
),就继续循环去检查后续的节点。
在循环体内部,我们需要对当前节点(也就是 cur
指针指向的节点)的下一个节点(通过 cur->next
访问)的值进行判断。如果发现 cur->next->val == val
,这就意味着找到了一个需要删除的节点,此时我们通过改变指针的指向来实现节点删除,具体操作就是让 cur
指针的 next
指针跳过这个要删除的节点,直接指向它的下下个节点,即 cur->next = cur->next->next;
,这样就相当于把值为 val
的这个节点从链表中移除掉了,实际是改变了链表的内部指针连接关系,使得该节点不再属于链表的一部分。
如果当前节点的下一个节点的值不等于 val
,那就说明这个节点不需要被删除,此时我们只需要让 cur
指针往后移动一位,即 cur = cur->next;
,继续去检查下一个节点的情况就可以了。
(四)返回处理后的链表头节点
经过前面的遍历和节点删除操作后,最后我们需要返回处理后的链表的头节点。由于之前添加了虚拟头节点,所以真正的链表头节点现在是由虚拟头节点的 next
指针指向的,因此我们直接返回 dummy->next
就可以得到经过处理后的链表的正确头节点了,这个头节点指向的链表就是已经删除了所有值为 val
的节点后的新链表。
三、总结
通过以上步骤,我们利用添加虚拟头节点的技巧,配合循环遍历和指针操作,统一且有条理地实现了删除链表中所有满足特定值节点的功能。这种思路在处理链表相关问题,尤其是涉及节点删除、插入等操作时经常会用到,希望大家通过对这道题思路的详细分析,能够更好地掌握链表操作的技巧,灵活应对类似的编程挑战呀
具体代码
/**
* 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) {
if(!head)return head;
ListNode* cur=new ListNode(-1);
cur->next=head;
ListNode* pre=cur;
while(cur->next)
{
if(cur->next->val==val)cur->next=cur->next->next;
else cur=cur->next;
}return pre->next;
}
};
在单链表操作中,常常会遇到依据特定值移除链表中节点的需求。下面这段 C++ 代码实现了给定一个单链表头节点指针 head
以及一个整数 val
,将链表中所有值为 val
的节点移除的功能。
首先是边界情况处理,if(!head) return head;
这句代码考虑到了传入的链表为空(即 head
为 nullptr
)的情况,此时无需操作,直接返回原 head
即可。
接着创建了一个虚拟头节点 ListNode* cur = new ListNode(-1);
,并让其 next
指针指向真正的链表头节点 cur->next = head;
,同时定义 ListNode* pre = cur;
,这里的虚拟头节点作用很大,能统一后续移除节点的逻辑,避免对头节点单独判断。
核心的移除节点操作在 while(cur->next)
循环里,只要当前节点(cur
指向)的下一个节点存在就继续循环。循环体中通过 if - else
判断,若 cur->next->val == val
,执行 cur->next = cur->next->next
,即跳过值为 val
的节点,改变链表指针连接实现 “删除”;若不符合条件,则执行 cur = cur->next
,继续检查下一个节点。
最后通过 return pre->next;
返回经过处理后的链表头节点,因为添加了虚拟头节点,真正的链表头就在 pre
的 next
指针指向处。总之,这段代码借助虚拟头节点,以清晰的逻辑和简洁的方式实现了单链表中指定值节点的移除功能,实用性很强哦。