【LeetCode】19. 删除链表的倒数第 N 个结点

leetcode

 

删除链表的倒数第 n 个节点,可以通过​​双指针法​​高效实现。该方法只需一次遍历,时间复杂度为 O(L)(L 为链表长度),空间复杂度为 O(1)。以下是具体实现与解析:


核心思路与步骤

1. ​​哑节点(Dummy Node)技巧​​

使用哑节点作为辅助头节点,可以避免处理删除头节点的特殊逻辑,例如当 n = L(删除原头节点)或链表长度为 1 时的情况。

2. ​​快慢指针的间距控制​​

  • ​​快指针​​先移动 n 步,与​​慢指针​​形成 n 步的间距。
  • 同步移动两指针直至​​快指针到达链表末尾​​,此时​​慢指针的下一个节点​​即为倒数第 n 个节点。

3. ​​删除操作​​

将慢指针的 Next 指向目标节点的下一个节点,跳过目标节点即可完成删除。


代码实现

type ListNode struct {
    Val  int
    Next *ListNode
}

func removeNthFromEnd(head *ListNode, n int) *ListNode {
    dummy := &ListNode{Next: head}  // 哑节点简化头节点删除逻辑
    fast, slow := dummy, dummy

    // 快指针先移动n步
    for i := 0; i <= n; i++ {       // 多走一步,使慢指针停在目标节点的前驱位置
        fast = fast.Next
    }

    // 同步移动快慢指针直到链表末尾
    for fast != nil {
        fast = fast.Next
        slow = slow.Next
    }

    // 删除目标节点
    slow.Next = slow.Next.Next      // 直接跳过目标节点
    return dummy.Next               // 返回哑节点的下一个节点作为新头节点
}

关键逻辑解析

  1. ​​哑节点的必要性​​
    当需要删除头节点时(如示例 2),哑节点能统一操作逻辑,避免单独处理 head 指针的特殊情况。

  2. ​​快指针多走一步的优化​​
    快指针初始多走一步(i <= n),使慢指针最终停留在​​目标节点的前驱节点​​,从而直接通过 slow.Next = slow.Next.Next 完成删除。

  3. ​​边界条件处理​​

    • ​​链表长度为 1 且 n=1​​:直接返回空链表。
    • ​​n 等于链表长度​​:删除头节点,哑节点的 Next 自动指向新头节点。

测试用例验证

示例 1:

输入:1 -> 2 -> 3 -> 4 -> 5, n = 2
输出:1 -> 2 -> 3 -> 5

​​过程​​:
快指针先移动 2+1=3 步至节点 4 → 同步移动至末尾 → 慢指针停在节点 3 → 删除节点 4。

示例 2:

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

​​过程​​:
快指针移动 1+1=2 步后为 nil → 慢指针停在哑节点 → 删除哑节点的下一个节点(原头节点)。


其他方法对比

方法时间复杂度空间复杂度适用场景
双指针法 O(L) O(1) 高效,推荐使用
计算链表长度 O(2L) O(1) 逻辑简单但需两次遍历
O(L) O(L) 需额外存储空间

总结

双指针法通过哑节点和间距控制,实现了高效的链表倒数节点删除。

posted @ 2025-04-16 12:04  云隙之间  阅读(55)  评论(0)    收藏  举报