LeetCode 237中等链表
删除链表中的节点 图解题解
这道题到底在问什么
给定链表里的一个非尾节点 node(不给头节点、也够不到它的前驱),要求只通过 node 这个引用把它从链表里删掉。
- 输入
- head=[4,5,1,9],待删 node 是值 5 的节点
- 输出
- [4,1,9]
最优解:一步一步想明白
- 3记住这八个字「复制后继、摘掉后继」,下面每一帧都在套它。顺序很重要:必须先复制值、再改指针。
- 4链表是 4→5→1→9,要删的 node 是值 5 这个节点。常规做法得找到它前面的 4、把 4 的 next 改成指向 1;可我们手里只有 node,够不到前面的 4,这条路走不通。
- 5换个角度,看 node 的后继:值 1 的节点(记作 succ)。既然删不掉 node 自己,那就把 succ 的内容搬到 node 上,再把 succ 删掉,从结果看一模一样。
- 6第一步,复制值:把 succ 的值 1 写进 node。node 从 5 变成了 1,链表暂时是 4→1→1→9,出现两个 1,其中第一个 1 就是原来的 node。
- 7第二步,改指针:让 node 的 next 跳过 succ、直接指向 succ 的后继(值 9)。succ(标红的第二个 1)就此脱离了链条。
- 8succ 被摘出去后,链表收成 4→1→9。原来值 5 的位置现在是值 1,后面接着 9,中间那个多余的 1 已经没人指向它了。
- 9删除完成:链表变成 4→1→9,等效地把原来的值 5 节点删掉了。全程没碰前驱、没遍历,只做了「复制一个值、改一个指针」两件事,O(1) 完成。
- 10再看一个巩固:链表 2→4→6→8,删值 4 的节点。同样的两步走,自己跟着推一遍。
- 11复制值:把后继 succ(值 6)的值搬进 node,node 由 4 变 6,链表暂成 2→6→6→8。
- 12改指针并摘掉 succ:node 的 next 跳到值 8,多余的那个 6 被摘除,链表收成 2→6→8。删值 4 的节点完成,套路一模一样。
⚠️ 容易写错的地方
✗ 错:想去找前驱再改它的 next
✓ 对:复制后继值 + 摘掉后继
题目只给 node、够不到前驱,常规删法在这里行不通
✗ 错:先改 next 再复制值
✓ 对:必须先复制值、再改 next
先断开就丢了后继的值,复制不到了,顺序不能反
✗ 错:以为真把 node 这个对象删了
✓ 对:其实是把后继的内容搬进 node、删的是后继
从链表结构看效果等同删 node,但被回收的是后继节点
完整代码(Python / C++ / Java)
Python
from typing import Optional, List
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def deleteNode(self, node: ListNode) -> None:
node.val = node.next.val
node.next = node.next.nextC++
#include <algorithm>
#include <stack>
#include <vector>
using namespace std;
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:
void deleteNode(ListNode* node) {
node->val = node->next->val;
node->next = node->next->next;
}
};Java
import java.util.*;
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
class Solution {
public void deleteNode(ListNode node) {
node.val = node.next.val;
node.next = node.next.next;
}
}复杂度
时间
O(1)
只做一次赋值和一次指针修改,与链表长度无关
空间
O(1)
没有开辟额外结构,只动了两个字段
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 删除链表中的节点 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
为什么不能用这个方法删尾节点?+
因为它依赖 node.next:要复制 node.next.val、要把 node.next 改成 node.next.next。尾节点的 next 为空,这两步都没法做。删尾节点只能找到它的前驱、把前驱的 next 置空。
这样删之后,原来的后继节点怎么办?+
它的值已经被复制走、也没有任何指针再指向它,就成了游离节点,交给垃圾回收(C++ 里通常要手动 delete 它)。所以严格说我们「删」的是后继,只是结果上等同于删了 node。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 删除链表中的节点 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。