删除链表的倒数第 N 个节点:一次遍历,双指针的经典距离模型

20 阅读3分钟

LeetCode 19|中等
核心思想:哑节点 + 快慢指针 + 固定距离
本文目标:不仅会写,还要真正理解“为什么 fast 要先走 n+1 步”


一、题目回顾

给你一个链表 head,删除链表的 倒数第 n 个节点,并返回新的头节点。

限制条件很关键:

  • 只能从前往后遍历
  • 要求 一次遍历完成
  • 单链表,没有前驱指针

二、为什么这道题不“简单”?

如果是数组:

  • 倒数第 n 个,直接算下标

但链表的问题在于:

  • 你不知道链表长度
  • 你不能反向遍历
  • 你无法直接定位“倒数”

所以这道题的核心不是删除,而是:

如何在一次遍历中,定位到“待删除节点的前一个节点”


三、整体解题思路

整个解法可以拆成三步:

  1. 引入哑节点(dummy),统一删除逻辑
  2. 使用快慢指针,制造一个“固定距离”
  3. fast 到达末尾时,slow 刚好停在待删除节点的前一个位置

四、完整 Java 实现

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;

        ListNode fast = dummy;
        ListNode slow = dummy;

        // fast 先走 n+1 步
        for (int i = 0; i <= n; i++) {
            fast = fast.next;
        }

        // fast 和 slow 同时走
        while (fast != null) {
            fast = fast.next;
            slow = slow.next;
        }

        // 删除目标节点
        slow.next = slow.next.next;

        return dummy.next;
    }
}

五、为什么一定要引入 dummy 节点?

ListNode dummy = new ListNode(0);
dummy.next = head;

这是一个工程级别的技巧,目的只有一个:

统一删除逻辑,包括删除头节点

例如:

head = [1], n = 1

如果没有 dummy:

  • 删除头节点需要特判
  • 代码会被 if/else 污染

有了 dummy:

  • 删除任何节点都变成一句话

    slow.next = slow.next.next;
    

六、这道题的灵魂:为什么 fast 要先走 n+1 步?

这一点是很多人写对了,但没真正理解的地方

1. 我们真正想要的是谁?

不是倒数第 n 个节点,而是:

倒数第 n 个节点的前一个节点

因为单链表删除节点,必须知道它的前驱。


2. fast 和 slow 的“距离设计”

for (int i = 0; i <= n; i++) {
    fast = fast.next;
}

这一步让:

fast 和 slow 之间,始终保持 n+1 个节点的距离

这个距离在后续过程中是一个不变量


3. 同步移动时发生了什么?

while (fast != null) {
    fast = fast.next;
    slow = slow.next;
}
  • fast 向链表末尾冲

  • slow 被“拖着走”

  • 当 fast 到达 null 时:

    • slow 刚好站在「待删除节点的前一个位置」

这不是巧合,而是由“固定距离”严格保证的。


七、用具体例子走一遍

链表:

1 → 2 → 3 → 4 → 5
n = 2

目标:删除 4

加入 dummy 后:

dummy → 1 → 2 → 3 → 4 → 5

fast 先走 n+1 = 3 步

fast 在 3
slow 在 dummy

同步移动

fastslow
41
52
null3

此时:

  • slow 在 3
  • slow.next 正是 4
  • 删除动作精准完成

八、时间和空间复杂度

  • 时间复杂度:O(L)

    • L 为链表长度,只遍历一次
  • 空间复杂度:O(1)

    • 只使用了常量级指针

九、这道题在双指针体系中的位置

这是一个**“距离型双指针”**的经典模型:

  • 不是快慢找中点
  • 不是判断是否成环
  • 而是:
    通过制造相对距离,获得定位能力

同一思想还可以解决:

  • 倒数第 k 个节点
  • 链表中点
  • 删除指定位置节点

十、一句话总结

这道题真正教会你的不是删除节点,而是:

在“只能向前”的链表结构中,用相对距离,换取精准定位。

理解这一点,双指针就不再是模板,而是工具。