19. 删除链表的倒数第 N 个结点(中等)

Blind 75 › 19. 删除链表的倒数第 N 个结点(中等) (第 5 章)
文章工具

题目

给定一条单链表,删除「从表尾往前数第 n 个」结点,并返回新链表的头结点。

这里的 n 从 1 开始:n = 1 表示最后一个结点,n = 链表长度 表示头结点。

自造示例:head = [10,20,30,40],n = 3 时从尾部倒数依次是 40(第 1 个)、30(第 2 个)、20(第 3 个),所以删除 20,输出 [10,30,40];若 n = 4,则删除头结点,输出 [20,30,40]。

约束:结点数 sz 满足 1 ≤ sz ≤ 30;结点值 0 ≤ Node.val ≤ 100;1 ≤ n ≤ sz。

思路

一句话:先让快指针走出 n 步的间隔,再让两个指针同步前进,快指针走到链表末尾之外时,慢指针刚好停在待删结点的前一个位置。

先想最直白的做法:把链表遍历一遍放进数组,按「倒数第 n 个」就是「正数第 sz - n + 1 个」来删除。这当然能做,但需要 O(sz) 额外空间;如果只是先用一遍遍历数出长度 sz,再用第二遍走到 sz - n 的位置删除,可以省掉数组,但要扫描两遍。

关键观察:链表不能像数组一样随机下标访问,但「倒数第 n 个」可以用两个指针的固定间隔表示——快指针先走 n 步,两针之间就隔了 n 个结点;之后两针同速前进,快指针越过最后一个结点、变成 nullptr / None 时,慢指针正好停在待删结点的前驱。

再补一个工程细节:如果待删的是头结点,头结点没有前驱,会让代码出现特判。引入一个虚拟头结点 dummy,让慢指针从 dummy 出发,就能把所有情况统一成「删除中间结点」。

解法

设链表结点数为 sz,步骤如下:

  1. 建虚拟头 dummy,令 dummy.next = head。
  2. 左指针 left 从 dummy 出发,右指针 right 从 head 出发。
  3. right 先走 n 步。
  4. left 与 right 同步前进,直到 right 越出链表末尾。
  5. 此时 left 在待删结点前一个位置,执行 left.next = left.next.next。
  6. 返回 dummy.next。

以 [10,20,30,40]、n = 3 为例:

阶段leftright说明
初始化dummy10le

🔒

本篇为付费内容

您已阅读试读部分,购买后即可解锁全文。

⚠️ 本站内容已做唯一性标记并受版权保护,未经授权禁止转载,侵权必究。

还没有评论,来说两句?

发表评论

欢迎交流技术细节;广告与人身攻击会被删除。

在线咨询