- 文档
- 教程
- 知识库
【免费下载链接】InterviewGuide
🔥🔥「InterviewGuide」是阿秀从校园->职场多年计算机自学过程的记录以及学弟学妹们计算机校招&秋招经验总结文章的汇总,包括但不限于C/C++ 、Golang、JavaScript、Vue、操作系统、数据结构、计算机网络、MySQL、Redis等学习总结,坚持学习,持续成长!
本文讲解阿秀精选力扣 300+ 算法题中的链表 Easy 档题目 237. 删除链表中的节点,核心是在不给你头指针 head、只给你待删节点本身的约束下完成节点删除。读完本文,你将掌握"值覆盖 + 跳过下一节点"的替身攻击删除法,理解它为什么只能用于非末尾节点,并能把这一技巧迁移到其他链表面试题中。
一、题目背景与题意理解
在 精选力扣 300+ 道算法题 系列中,链表分类下的 Easy 题目共有三道:21. 合并两个有序链表、206. 反转链表、237. 删除链表中的节点(完整索引见 链表分类介绍页)。本题是其中思路最"反直觉"的一道。
题目要求:请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。
现有一个链表head = [4,5,1,9],它可以表示为:
4 -> 5 -> 1 -> 9示例 1
输入: head = [4,5,1,9], node = 5 输出: [4,1,9] 解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.示例 2
输入: head = [4,5,1,9], node = 1 输出: [4,5,9] 解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.二、题目的四条硬性约束(务必逐条理解)
原题"说明"部分给出了四条约束,它们不是废话,而是本题解题思路的前提,缺一不可:
- 链表至少包含两个节点:保证被删节点后面一定还有节点可"顶替";
- 链表中所有节点的值都是唯一的:保证按值"复制"后不会与其他节点值冲突,也保证删除后结果唯一可验证;
- 给定的节点为非末尾节点并且一定是链表中的一个有效节点:这是替身攻击可行的最关键前提,末尾节点没有后继可借;
- 不要从你的函数中返回任何结果:函数签名是
void deleteNode(ListNode* node),直接在原链表上操作,无需、也无法返回新头结点(因为你根本拿不到 head)。
三、常规删除法为什么在这里失效
在一般认知里,删除单链表节点需要两步:
- 找到待删节点的前驱节点prev;
- 令
prev->next = node->next,再释放 node。
问题在于:找到前驱节点必须从 head 开始遍历。而本题的函数签名只给了ListNode* node,没有给 head,单链表又无法从当前节点"回溯"到前驱,因此"先找前驱再删"的常规思路在这里完全走不通。
四、核心思路:替身攻击(值覆盖 + 跳过下一节点)
既然物理上无法删除"自己"(没有前驱可改),那就换个思路——让下一个节点替自己去死:
- 第一步,把下一个节点的值
node->next->val复制到当前节点node->val,当前节点"变成"了下一个节点的值; - 第二步,让当前节点的 next 指针直接指向下下个节点
node->next->next,把下一个节点从链表中"跳过"。
从外部视角看,链表中"值等于原 node 值"的节点消失了,链表长度减一,功能与删除 node 完全等价。整个过程没有用到 head,时间复杂度 O(1),空间复杂度 O(1)。
以链表 4 -> 5 -> 1 -> 9、删除值为 5 的节点为例
删除前: 4 -> 5 -> 1 -> 9 (node 指向值为 5 的节点) 第 1 步: 把 node 的值改为 node->next 的值 1 4 -> 1 -> 1 -> 9 (注意这里 node 值已变) 第 2 步: node->next 指向 node->next->next 4 -> 1 -> 9 (值为 1 的第二个节点被跳过) 删除后: 4 -> 1 -> 9,与示例 1 输出一致五、仓库题解:第一版,替身攻击
阿秀在 237. 删除链表中的节点题解 中给出的 C++ 解法正是"替身攻击":
void deleteNode(ListNode* node) { node->val = node->next->val; node->next = node->next->next; }整个函数只有两行,逐行拆解:
| 行 | 代码 | 作用 |
|---|---|---|
| 第 1 行 | node->val = node->next->val; | 用后继节点的值覆盖当前节点,完成"身份替换" |
| 第 2 行 | node->next = node->next->next; | 越过后继节点,把后继节点从链表中摘除 |
阿秀在力扣中文站提交时的平台记录为:执行用时 12 ms,击败当时 98.53% 的 C++ 提交;内存消耗 9.2 MB,击败 47.23% 的提交。需要说明的是,这类百分比数据取决于当时的提交样本与平台统计口径,仅作为参考,重点在于该解法的时间、空间复杂度均为常数级。
关于内存释放的补充说明
在严格意义上,第 2 行跳过了后继节点后,被跳过的节点在 C++ 中并未显式delete。在力扣的评测环境下,节点由平台统一管理,不释放也不会影响正确性;但在实际工程项目中,若该节点由new分配且无其他引用,应谨慎考虑释放策略(例如将其delete后再把node->next指向node->next->next,注意操作顺序),避免内存泄漏。本题为了简洁,采用不释放的写法是力扣社区的主流做法。
六、为什么必须是非末尾节点
替身攻击成立的根本前提是node->next不为空:
- 若
node是末尾节点,则node->next为nullptr,第 1 行直接访问node->next->val会产生空指针解引用; - 退一步说,即使不崩溃,末尾节点后面也没有"替身"可以顶上来,值覆盖无从谈起。
这正是题目约束"给定的节点为非末尾节点"的原因——它保证了算法的安全性,也意味着本题的解法天然不适用于删除末尾节点。若面试官追问"删除末尾节点怎么办",答案是需要额外的标记位(如哑节点)或从 head 遍历找前驱,而这恰恰是题目刻意不给你 head 的原因:让你意识到链表删除操作的多样性。
七、等价实现(Java / Python / Go 思路一致)
仓库以 C++ 解法为主,以下给出基于同一思路的常见等价实现,供不同语言背景的读者对照练习:
// Java class Solution { public void deleteNode(ListNode node) { node.val = node.next.val; node.next = node.next.next; } }# Python class Solution: def deleteNode(self, node): node.val = node.next.val node.next = node.next.next// Go func deleteNode(node *ListNode) { node.Val = node.Next.Val node.Next = node.Next.Next }三种语言的核心逻辑与 C++ 完全一致:先覆盖值,再跳指针。这也说明替身攻击不是某种语言的奇技淫巧,而是对链表数据结构本质(指针/引用的可重定向能力)的通用利用。
八、易错点与面试追问清单
面试中围绕本题常见的考察点:
- 为什么能删"自己"?答:不物理删除当前节点,而是让后继节点"替身"被跳过,对外表现等价于删除了目标值。
- 为什么不能删末尾节点?答:无后继可借,
node->next->val会空指针解引用。 - 值唯一性约束的意义?答:保证"按值覆盖"后链表仍满足唯一性,删除结果无歧义,便于验证。
- 时间复杂度与空间复杂度?答:均为 O(1),不依赖链表长度,也不需要额外容器。
- 这个技巧还能用在哪?答:凡是"只给你一个中间节点、要求原地改造"的场景,都可考虑"把后继的内容搬过来再摘除后继"的思路。
九、仓库联动:链表 Easy 三件套一起刷
本题与同目录下的另外两道 Easy 题恰好构成链表基础操作的三个维度,建议按顺序刷完(入口见 链表分类介绍页):
- 21. 合并两个有序链表:掌握 ListNode 结构体的定义(
ListNode(int x) : val(x), next(nullptr) {})与迭代式链表拼接,理解链表的构建与重组; - 206. 反转链表:pre / curr / next 三指针迭代,理解链表的指针重定向;
- 删除链表中的节点:理解链表的原地删除及其约束。
三道题全部是 O(1) 空间、单次遍历级别的操作,是校招笔试面试中链表题的最小可复用技能包。如果时间紧张,可以参考 算法部分使用指南 按人群选择刷题路径。
十、小结
237 题表面只有两行代码,背后却是一次重要的思维转变:当常规手段(找前驱)被剥夺时,通过"值覆盖 + 指针跳跃"从结果上达成同一目标。这提醒我们,面试考链表删除时,考官不仅看你会不会写代码,更看你能不能跳出"必须物理删除该节点"的思维定式——替身攻击,正是对这种定式的精准打击。
- 文档
- 教程
- 知识库
【免费下载链接】InterviewGuide
🔥🔥「InterviewGuide」是阿秀从校园->职场多年计算机自学过程的记录以及学弟学妹们计算机校招&秋招经验总结文章的汇总,包括但不限于C/C++ 、Golang、JavaScript、Vue、操作系统、数据结构、计算机网络、MySQL、Redis等学习总结,坚持学习,持续成长!
相关推荐
Czkawka 磁盘清理工具完整教程:一次扫描定位重复文件与空文件夹
Czkawka 磁盘清理工具完整教程:一次扫描定位重复文件与空文件夹 Czkawka 是一款用 Rust 编写的免费开源磁盘清理工具,能把你指定的目录一次性扫一
桌面应用LeetCode 237 无头指针链表节点删除:LeetCode-Go 中的值拷贝解法与内存语义详解
LeetCode 237 无头指针链表节点删除:LeetCode Go 中的值拷贝解法与内存语义详解 导读 LeetCode 237(Delete Node i
示例工程LogicStack-LeetCode 题解精读:237. 删除链表中的节点——O(1) 时间"值覆盖"删除法的模拟与脑筋急转弯
LogicStack LeetCode 题解精读:237. 删除链表中的节点——O 1 时间"值覆盖"删除法的模拟与脑筋急转弯 本篇技术指南围绕 LogicSt
教程文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考