【LeetCode】19. 删除链表的倒数第 N 个结点
删除链表的倒数第 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 // 返回哑节点的下一个节点作为新头节点 }
关键逻辑解析
- 
哑节点的必要性
当需要删除头节点时(如示例 2),哑节点能统一操作逻辑,避免单独处理head指针的特殊情况。 - 
快指针多走一步的优化
快指针初始多走一步(i <= n),使慢指针最终停留在目标节点的前驱节点,从而直接通过slow.Next = slow.Next.Next完成删除。 - 
边界条件处理
- 链表长度为 1 且 
n=1:直接返回空链表。 - 
n等于链表长度:删除头节点,哑节点的Next自动指向新头节点。 
 - 链表长度为 1 且 
 
测试用例验证
示例 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) | 需额外存储空间 | 
总结
双指针法通过哑节点和间距控制,实现了高效的链表倒数节点删除。
                    
                
                
            
        
浙公网安备 33010602011771号