19. 删除倒数第 N 个结点

中等 含交互动画

学完这道你应该能: 说清它为什么用「快慢指针」,而不是死记步骤; 合上页面,自己默写出核心代码; 用 30 秒把思路讲清楚。

AI 私教 · 就着本题动画讲
卡住了不用硬扛,按 8 步把这题真正走通。
从读题、试解、找法到手写代码,边问边打勾,适合第一次系统学算法的同学。

题目描述

删除链表的倒数第 n 个结点,返回头结点。

输入 = 1 → 2 → 3 → 4 → 5, n = 2
输出 = 1 → 2 → 3 → 5 (删掉 4)

思路解析

先遍历数出长度 len,再从头走 len−n 步找到待删节点的前驱——逻辑没错,但要把链表走两趟。能不能一趟就搞定?

转折:从 dummy 哨兵出发,让 fast 先独自走 n+1 步,于是 fast 比 slow 领先 n+1 个节点。然后两个一起走,当 fast 跨出尾部(变 null)时,slow 正好落在「倒数第 n 个」的前一个,删它就方便了。

起点 · slow=fast=dummy(头):slowfast 都从哨兵后的头节点 1 出发(图上重合在头)。下面每一步分开移动看得清楚。

fast 先走第 1 步:fast 单独走第 1 步到 2,slow 还在 1。先把 fast 拉开。

fast 先走第 2 步 · 拉开间距 n:fast 再走第 2 步到 3。fast 比 slow 领先 n = 2 个节点(slow 在 1、fast 在 3)。这个间距之后会一直保持,两个指针同步走。

同步走第 1 步 · slow→2, fast→4:现在两个一起走一步:slow 到 2,fast 到 4。间距仍是 2,丝毫不变。

判循环条件 · fast 还没出界:进下一轮前先看条件 while fast:fast 在 4(还不是 null),条件成立,再走一步。slow 在 2、fast 在 4,间距还是 2。

同步走第 2 步 · fast 到尾:再一起走一步:slow 到 3,fast 到最后一个 5。下一步 fast 会跨出尾部变 null,while fast 不成立、循环停。slow 停在 3,它的下一个 del=4 正是倒数第 2 个——要删的就是它。

删除 · slow.next = slow.next.next:执行 slow.next = slow.next.next:把 4 摘掉(虚线划掉),3 的箭头越过它直接接到 5。链表变成 1 → 2 → 3 → 5

边界 · 若要删的是头结点:反向想个边界:若 n=5 要删倒数第 5 个(就是头节点 1)。多亏从 dummy 哨兵出发,slow 会停在 dummy 上,dummy.next 越过 1 接到 2,头节点照样能删——这正是哨兵存在的意义。

让两个指针保持 n 的间距一起走,领头的到尾、跟随的就到了「倒数第 n」的位置——这把「从后数」巧妙转成「一起走到头」。加哨兵则让删头不再是特例。

参考代码(Python)

Python
1def removeNthFromEnd(self, head, n):
2    dummy = ListNode(0, head)             # 哨兵,统一处理删头
3    slow = fast = dummy
4    for _ in range(n + 1):                # fast 先走 n+1
5        fast = fast.next
6    while fast:                           # 同步走到 fast 出界
7        slow = slow.next
8        fast = fast.next
9    slow.next = slow.next.next            # 摘除倒数第 n 个
10    return dummy.next

复杂度分析

  • 时间复杂度:O(L) —— L 是链表长度,指针总共扫一趟
  • 空间复杂度:O(1) —— 只用 dummy、slow、fast 几个指针

套路模板

记住骨架:哨兵 + fast 先走 k+1 步 + 同步到出界,slow 就停在倒数第 k 的前驱。删除、找倒数第 k 个都套它。

Python
1dummy = ListNode(0, head)             # 删除类先挂哨兵
2slow = fast = dummy
3for _ in range(k + 1):                # 先拉开 k+1 的间距
4    fast = fast.next
5while fast:                           # 一起走到 fast 出界
6    slow, fast = slow.next, fast.next
7# 此时 slow 在倒数第 k 个的前一个

易错点

  • 错误写法:fast 只先走 n 步、循环用 while fast.next正确写法:从 dummy 走 n+1 步、循环用 while fast(两种写法都对但必须配套:基准点和步数错配 1,slow 就会停错位置删错节点)
  • 错误写法:不加哨兵,删头时崩正确写法:用 dummy 指向 head(删的是头结点时它没有前驱,哨兵恰好补上这个前驱)

以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握

下一题 →83. 删除排序链表重复元素 ← 返回题库