链表元素移除:头指针法与虚拟头节点法

📅 2026/7/14 4:22:26
链表元素移除:头指针法与虚拟头节点法
目录移除链表元素头指针方法判断并处理头节点删除非头节点结束与返回完整代码参考 (C/C)移除链表元素虚拟头节点方法初始化虚拟头节点与遍历指针遍历并删除目标节点结束、清理与返回完整代码参考 (C/C)移除链表元素头指针方法在处理链表删除操作时头指针方法是一种直接针对原链表进行操作的方法。它主要分为两个阶段首先处理开头的连续待删除节点然后遍历处理后续的非头节点。1. 判断并处理头节点首先我们需要判断头节点是否需要删除。核心步骤条件检查判断头指针head是否为空同时判断头节点的值是否等于目标值target。如果head不为空并且head-val target则进入循环进行删除否则说明当前头节点不需要删除。保存与断开如果满足删除条件先定义一个临时指针tmp保存当前头节点的地址方便后续释放该节点的内存。然后让head指向下一个节点即head head-next使链表入口向后移动。释放内存删除tmp保存的原头节点释放该节点占用的内存。为什么使用while循环而不是if因为可能存在连续多个需要删除的头节点例如链表为[1, 1, 1, 2, 3]且目标值为1。使用while循环可以不断移动head直到新的头节点为空或者新的头节点值不等于目标值为止。2. 删除非头节点当头节点确定不需要删除后我们开始遍历并删除链表中的其他非头节点。核心步骤初始化遍历指针定义一个cur指针让它指向当前的头节点head。注意cur中存放的是当前节点的地址当前节点中的next指针才存放下一个节点的地址。循环条件判断cur不为空并且cur-next不为空时进入循环。因为我们删除的是cur后面的节点所以必须保证当前节点存在并且当前节点后面还有节点可以检查。值比对与删除情况 Acur-next指向的节点值等于目标值说明找到了需要删除的节点。首先使用临时指针tmp保存cur-next的地址即待删除节点的地址方便之后释放内存。修改当前节点的next指针跳过待删除节点cur-nextcur-next-next;释放tmp保存的节点内存。情况 Bcur-next指向的节点值不等于目标值说明当前节点后面的节点不需要删除此时让cur向后移动继续检查后面的节点curcur-next;3. 结束与返回当循环结束时说明cur已经到达链表尾部或者cur后面没有需要继续检查的节点。此时链表中的所有目标节点都已经删除完成最后返回head作为新链表的入口。4. 完整代码参考 (C/C)structListNode{intval;ListNode*next;ListNode(intx):val(x),next(NULL){}};ListNode*removeElements(ListNode*head,intval){// 1. 处理头节点需要删除的情况while(head!NULLhead-valval){ListNode*tmphead;headhead-next;deletetmp;}// 2. 处理非头节点需要删除的情况ListNode*curhead;while(cur!NULLcur-next!NULL){if(cur-next-valval){ListNode*tmpcur-next;cur-nextcur-next-next;deletetmp;}else{curcur-next;}}// 3. 返回新链表的头指针returnhead;}移除链表元素虚拟头节点方法在处理链表删除操作时虚拟头节点Dummy Head方法是一种非常优雅且通用的技巧。它的核心思想是通过在原链表前人为增加一个辅助节点使得原头节点变成了普通节点从而能够用同一套逻辑统一处理链表中的所有节点无需单独为头节点编写特判逻辑。1. 初始化虚拟头节点与遍历指针首先我们需要构建虚拟头节点并初始化用于遍历链表的指针。核心步骤创建虚拟头节点首先在堆或栈上创建一个虚拟头节点例如dummyHead其值可以为任意值通常设为 0 或 -1。连接原链表将虚拟头节点的next指针指向原链表的头节点head即dummyHead-next head。这样原头节点就变成了虚拟头节点的下一个节点。初始化遍历指针定义一个cur指针让它指向虚拟头节点dummyHead。此时cur中存放的是虚拟头节点的地址用于从头开始遍历和检查链表。2. 遍历并删除目标节点由于有了虚拟头节点所有可能被删除的节点包括原头节点现在都处于某个节点的后面。我们可以直接使用统一的循环来检查和删除节点。核心步骤循环条件判断cur-next是否为空。如果不为空则进入循环。因为我们是通过当前节点去检查和删除它的下一个节点所以必须保证下一个节点是真实存在的。值比对与删除情况 Acur-next指向的节点值等于目标值说明找到了需要删除的节点。首先使用临时指针tmp保存cur-next的地址即待删除节点的地址方便之后释放内存。修改当前节点的next指针让当前节点跳过待删除节点直接指向下下一个节点cur-nextcur-next-next;释放内存删除tmp保存的节点释放其占用的内存。注意此时cur指针不需要向后移动因为新接上来的cur-next节点还没有经过检查下一轮循环需要继续检查它。情况 Bcur-next指向的节点值不等于目标值说明当前节点后面的节点不需要删除此时让cur向后移动继续检查后面的节点curcur-next;关于链表结束状态的准确理解当循环因为cur-next NULL而退出时cur本身并不是空的它此时正好存放的是新链表中最后一个尾节点的地址。因为是最后一个节点它身后没有其他节点了所以它的next为空。此时链表已全部检查完毕。3. 结束、清理与返回当遍历完成链表中的所有目标节点都已被成功剔除最后我们需要做收尾工作。核心步骤获取新头节点将虚拟头节点存放的下一个节点地址即dummyHead-next保存下来作为新链表的真正入口。释放虚拟头节点由于虚拟头节点是我们在函数内部为了方便操作而额外创建的在返回前必须手动将其删除并释放内存避免内存泄漏。返回结果返回刚才保存的新头节点指针。4. 完整代码参考 (C/C)structListNode{intval;ListNode*next;ListNode(intx):val(x),next(NULL){}};ListNode*removeElements(ListNode*head,intval){// 1. 创建虚拟头节点并连接原链表ListNode*dummyHeadnewListNode(0);dummyHead-nexthead;// 初始化遍历指针指向虚拟头节点ListNode*curdummyHead;// 2. 统一遍历并删除目标节点while(cur-next!NULL){if(cur-next-valval){ListNode*tmpcur-next;cur-nextcur-next-next;// 跳过待删除节点deletetmp;// 释放内存// 注意这里 cur 不需要移动下一轮继续检查新的 cur-next}else{curcur-next;// 节点不需要删除cur 向后移动}}// 3. 妥善返回并清理虚拟头节点ListNode*newHeaddummyHead-next;deletedummyHead;// 释放虚拟头节点内存returnnewHead;}