链表题解——删除链表的倒数第 N 个结点【LeetCode】
19. 删除链表的倒数第 N 个结点
一、算法逻辑通顺讲解(逐步推理每一步)
1️⃣ 引入虚拟头节点,初始化双指针
首先,创建一个虚拟节点 dummy_head
并指向链表头节点,这样可以保证即使删除的是头节点,也不需要特殊处理。接着,定义两个指针 fast
和 slow
,初始化都指向 dummy_head
。
2️⃣ 快指针先行 n+1 步
接着让 fast
指针先向前移动 n + 1
步,提前与 slow
指针拉开一个间距,使得两指针之间相差 n
个节点。之所以是 n + 1
,是为了最终 slow
停在 目标节点的前一个节点,以便进行删除操作。
3️⃣ 双指针同步前进
然后同时移动 slow
和 fast
两个指针,直到 fast
指向链表末尾(即 None
)。此时,slow
正好指向倒数第 n
个节点的前一个节点。
4️⃣ 执行删除操作
此时将 slow.next
指向 slow.next.next
,即跳过原来的第 n
个节点,完成删除。
5️⃣ 返回链表新的头节点
最后返回 dummy_head.next
,即删除操作后的链表头节点。
二、核心点总结
-
✅ 双指针+间距:让快指针先走
n+1
步,再同步前进,从而让慢指针精确指向倒数第n
个节点的前一个节点。 -
✅ 虚拟头节点:极大地简化了链表头节点被删除时的边界处理。
-
✅ 一次遍历完成删除操作:只需一趟扫描,不需要先获取链表长度,节省效率。
class Solution:def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:# 创建一个虚拟节点,并将其下一个指针设置为链表的头部dummy_head = ListNode(0, head)# 创建两个指针,慢指针和快指针,并将它们初始化为虚拟节点slow = fast = dummy_head# 快指针比慢指针快 n+1 步for i in range(n+1):fast = fast.next# 移动两个指针,直到快速指针到达链表的末尾while fast:slow = slow.nextfast = fast.next# 通过更新第 (n-1) 个节点的 next 指针删除第 n 个节点slow.next = slow.next.nextreturn dummy_head.next
三、时间复杂度分析
-
快指针走了
n+1
步,双指针最多再走L - n - 1
步,总共一次遍历链表。 -
所以总体是一次完整的链表遍历。
时间复杂度:O(L),其中 L 是链表长度。
四、空间复杂度分析
-
仅使用了三个指针变量
dummy_head
、fast
和slow
,没有使用任何额外的数据结构。
空间复杂度:O(1)(常数空间)
✅ 总结一句话
该算法通过引入虚拟节点和快慢指针配合,以一次遍历、常数空间的方式高效地完成链表中倒数第 n
个节点的删除,关键在于构造 n+1 的间距与双指针同步推进。