链表中的“神行太保”:如何优雅地删除倒数第N个节点(只遍历一次!)
摘要:本文详解LeetCode 19题“删除链表的倒数第N个节点”。通过“双指针”技巧和“虚拟头节点”的巧妙配合,只需一次遍历即可解决问题,带你彻底搞懂链表操作中的边界处理艺术。
📚 核心知识点:双指针与虚拟头节点
在处理链表问题时,我们经常会遇到两个痛点:
- 不知道链表总长度:无法直接通过下标定位倒数第N个节点。
- 删除头节点的麻烦:如果要删除的是链表的第一个节点,普通的遍历逻辑容易失效。
为了解决这两个问题,我们需要掌握两个神器:
- 双指针(快慢指针) :
想象两个跑步的人,快指针(Fast) 先跑 N 步,然后慢指针(Slow) 才开始跑。当快指针跑到终点时,慢指针和快指针之间正好相差 N 步,此时慢指针的位置就是我们要找的“倒数第 N 个节点”的前驱位置。 - 虚拟头节点(Dummy Node) :
在链表头部前加一个“替身”节点,指向真正的头节点。这样无论我们要删除的是中间节点还是头节点,操作逻辑都是一致的(都是删除slow.next),避免了复杂的边界判断。
📝 题目解析:LeetCode 19. 删除链表的倒数第 N 个结点
题目描述:
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
(解释:倒数第2个节点是 4,删除后链表变为 1->2->3->5)
💡 解题思路:拉开距离,同步前行
步骤演示:
- 建立虚拟头节点:
dummy -> 1 -> 2 -> 3 -> 4 -> 5。 - 双指针就位:
fast和slow都指向dummy。 - 拉开距离:让
fast先走 N 步。此时fast领先slowN 个节点。 - 同步前行:
fast和slow同时向后移动,直到fast到达链表末尾(即fast.next为空)。 - 锁定目标:此时
slow刚好停在倒数第 N 个节点的前一个位置。 - 执行删除:
slow.next = slow.next.next。
💻 代码实现(Python)
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
# 1. 创建虚拟头节点,指向head,解决删除头节点的边界问题
dummy = ListNode(0, head)
# 2. 初始化快慢指针,都指向虚拟头节点
fast = dummy
slow = dummy
# 3. 快指针先走 n 步
# 这样快慢指针之间就拉开了 n 的距离
for _ in range(n):
fast = fast.next
# 4. 快慢指针同时移动,直到快指针到达链表尾部
# 注意:判断条件是 fast.next,这样 slow 会停在“待删除节点”的前一个位置
while fast.next:
slow = slow.next
fast = fast.next
# 5. 执行删除操作
# 此时 slow.next 就是要删除的节点
slow.next = slow.next.next
# 6. 返回真正的头节点(dummy.next)
return dummy.next
🔍 深度图解:为什么要停在“前一个”位置?
假设链表是 1 -> 2 -> 3 -> 4 -> 5,我们要删除倒数第 2 个节点(即 4)。
-
初始状态:
dummy -> 1 -> 2 -> 3 -> 4 -> 5,fast和slow都在dummy。 -
快指针先行:
n=2,fast走两步,指向2。 -
同步移动:
- 第一轮:
fast到3,slow到1。 - 第二轮:
fast到4,slow到2。 - 第三轮:
fast到5,slow到3。
- 第一轮:
-
终止条件:此时
fast.next为空,循环结束。 -
结果:
slow停在3,slow.next是4。执行slow.next = slow.next.next,即3直接指向5,成功删除4。
📌 总结与拓展
- 为什么要用 Dummy Node?
如果链表只有一个节点[1],且要删除倒数第 1 个节点。如果没有dummy,slow指针很难处理“删除头节点”的情况(因为头节点没有前驱)。用了dummy,逻辑统一为“删除slow的下一个节点”,代码更健壮。 - 空间复杂度:,只使用了常数级别的额外空间。
这道题是“双指针”技巧的经典应用,掌握了这个“拉开距离法”,以后遇到“查找倒数第 K 个节点”或者“寻找链表中点”的问题都能迎刃而解!
爱摸鱼的打工仔:关注我,每天分享一个提升效率的算法小技巧!🐟