【LeetCode 19】力扣算法:删除链表的倒数第 N 个结点 —— 快慢指针法
题目:给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
提示:
- 链表中结点的数目为 sz
- 1 <= sz <= 30
- 0 <= Node.val <= 100
- 1 <= n <= sz

核心思想:
要删除链表的倒数第 n 个节点,我们可以使用双指针法。这种方法的关键在于使用两个指针,一个快指针先走 n 步,然后两个指针一起走,当快指针到达链表末尾时,慢指针就恰好指向了需要删除的节点的前一个节点。
算法步骤:
-
初始化:创建虚拟头结点;创建两个指针 fast 和 slow,初始值为虚拟头结点。
-
快指针先行:让 fast 指针先前进 n 步。如果 fast.next 为 null,说明要删除的是头节点,直接处理这种情况。
-
双指针前进:同时移动 fast 和 slow 指针,直到 fast 到达链表末尾。
-
删除节点:此时 slow 指向的就是需要删除的节点的前一个节点,调整 slow.next 指向 slow.next.next,从而删除了倒数第 n 个节点。
-
返回结果:返回处理后的链表头节点。
复杂度分析:
-
时间复杂度:O(sz),其中 sz 是链表的长度。我们最多遍历链表两次。
-
空间复杂度:O(1),我们只使用了两个指针,所以额外空间复杂度是常数级别的。
做这个题目时,一开始我犯了个错误,我不使用虚拟头结点,直接让快慢指针指向head的头结点,然后按步骤删除,结果是报错。我想了很久,这个方法明明理论上是没有问题的,为什么会报错呢?最后我发现我把快慢指针初始为head的头结点的做法,不能处理当删除值是头结点的情况。当测试用例要删除的结点是头结点时,会直接报错,因为没有处理这种特殊情况。
所以最好且最方便的做法就是创建虚拟头结点,让快慢指针初始值为虚拟头结点,才能防止特殊情况。
正确的 Java 代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
// 创建虚拟头结点,防止需要删除的是头结点的情况
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode slow = dummyHead;
ListNode fast = dummyHead;
// 步骤一:先让快指针走n步
for(int i=0; i<n; i++){
fast = fast.next;
}
// 步骤二:快慢指针同时走,直到快指针到达最后一个结点
while(fast.next != null){
fast = fast.next;
slow = slow.next;
}
// 步骤三:删除慢指针的后一个结点
slow.next = slow.next.next;
return dummyHead.next;
}
}
所有正文内容皆为本人原创,禁止搬运

浙公网安备 33010602011771号