0019.删除链表的倒数第N个结点

难度:🟡 中等

标签链表双指针

链接19. 删除链表的倒数第 N 个结点

题目描述

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

示例 1:

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

输入:head = [1], n = 1
输出:[]

示例 3:

输入:head = [1,2], n = 1
输出:[1]

解题思路

核心思想

本题的核心是在一次遍历中找到并删除倒数第 n 个节点。要删除一个节点,我们必须找到它的 前驱节点。因此,问题转化为:如何通过一次遍历,定位到倒数第 n 个节点的前驱节点。

思路选择

快慢指针 + 虚拟头节点(Dummy Head) 是解决此问题的标准最优思路。

  • 快慢指针:我们可以设置两个指针,fastslow。先让 fast 指针比 slow 指针领先 n 步。然后,两个指针同步前进。当 fast 到达链表末尾时,slow 正好指向倒数第 n 个节点。

  • 虚拟头节点:为了优雅地处理“删除头节点”这一边界情况,我们可以创建一个 dummy 节点,让它的 next 指向原始的 head。我们让 slow 指针从 dummy 开始,这样当 fast 到达末尾时,slow 就恰好位于待删除节点的前一个位置,便于执行删除操作。

关键步骤

  1. 创建虚拟头节点let dummy = new ListNode(0, head)

  2. 初始化指针fast 指向 headslow 指向 dummy

  3. 拉开距离:让 fast 指针先向前移动 n 步。

  4. 同步移动:同时向前移动 fastslow 指针,每次都移动一步,直到 fast 指针到达链表末尾 (fast === null)。

  5. 执行删除:此时 slow 正是待删除节点的前驱节点。执行 slow.next = slow.next.next 来跳过待删除节点。

  6. 返回结果:返回 dummy.next,它指向新链表的头节点。

代码实现 (推荐)

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 * this.val = (val===undefined ? 0 : val)
 * this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} n
 * @return {ListNode}
 */
var removeNthFromEnd = function(head, n) {
    const dummyHead = new ListNode(0, head);
    let slow = dummyHead, fast = head;

    // 快指针先走 n 步
    for(let i = 0; i < n; i++) {
        fast = fast.next;
    }

    // 快慢指针同步走,直到快指针到达终点
    while (fast !== null) {
        fast = fast.next;
        slow = slow.next;
    }

    // 此时 slow 指向待删除节点的前一个节点
    slow.next = slow.next.next;

    return dummyHead.next;
};

复杂度分析

  • 时间复杂度O(L) 其中 L 是链表的长度。我们只需要对链表进行一次完整的遍历。

  • 空间复杂度O(1) 我们只使用了常数个额外变量作为指针,空间消耗是恒定的。

相关题目

总结

本题是双指针和虚拟头节点技巧结合的经典案例。通过快慢指针建立固定距离差,可以优雅地定位到倒数第 N 个节点的前驱。而虚拟头节点的引入,则统一了删除操作的逻辑,避免了对“是否删除头节点”进行繁琐的 if-else 判断,是链表问题中非常实用的技巧。

Copyright © Jun 2025 all right reserved,powered by Gitbook该文件修订时间: 2025-07-03 17:35:08

results matching ""

    No results matching ""

    results matching ""

      No results matching ""