踩坑记录[6]——LeetCode 19题:删除链表的倒数第 N 个结点
题目描述
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
输出:[]
示例 3:
输入:head = [1,2], n = 1
输出:[1]
提示:
- 链表中结点的数目为
sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
进阶:你能尝试使用一趟扫描实现吗?
答案(C++语言,已通过LeetCode测试)
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *p = new ListNode(-1);
p->next = head;
ListNode *fast = p, *slow = p;
for(int i=0;i<n+1;i++)
fast = fast->next;
while(fast != nullptr){
slow = slow->next;
fast = fast->next;
}
slow->next = slow->next->next;
return p->next;
}
};
方案描述
在这个问题中,我们需要删除链表中的倒数第n个节点。为了找到倒数第n个节点的前一个节点(也就是倒数第n+1个节点),我们使用了双指针的方法。
首先,我们创建了一个虚拟头节点 p
,将其指向原始链表的头节点 head
。这样做的目的是为了处理特殊情况,例如链表长度为1或者删除的是头节点的情况。接下来,我们使用两个指针 fast
和 slow
,都初始化为虚拟头节点 p
。
然后,我们让 fast
指针先向前移动n+1步,使其指向第n+1个节点。为什么要向前移动n+1步而不是n步呢?因为我们要找的是倒数第n个节点的前一个节点,所以 fast
指针需要多向前移动一步。在 fast
指针移动的过程中,我们需要确保 fast
指针不会越界,因此需要对链表长度不足n+1的情况进行处理,这也是为什么我们使用了虚拟头节点。
接下来,我们让 fast
指针和 slow
指针同时向前移动,直到 fast
指针指向链表的最后一个节点为止。这样,slow
指针就指向了倒数第n+1个节点。
然后,我们将倒数第n+1个节点的 next
指针指向其下一个节点的下一个节点,从而删除了倒数第n个节点。
最后,我们返回虚拟头节点 p
的下一个节点,即为删除倒数第n个节点后的链表头节点。
踩坑记录
题目难度:中等
本题目为寻找链表中倒数第n个结点的典型例题,核心解题思想:定义两个指针快慢指针,先让快指针走n步,剩下m-n步,两个指针一起走,就能到第m-n个结点(也就是倒数第n个结点)!
-
要寻找倒数第n+1个结点而不是倒数第n个
由于本题的目的是删除倒数第n个结点,因此需要找到这个结点的前一个结点,也就是倒数第n+1个结点。因此,在
fast
指针前进时,前进了n+1步,for(int i=0;i<n+1;i++) // n+1 fast = fast->next;
-
使用虚拟头节点避免空指针报错
若链表长度为1或者删除的是头节点,可能会导致指针取
fast->next
报错。于是,刚开始时,建立虚拟头节点,该结点的下一个结点为实际的头节点。ListNode *p = new ListNode(-1); p->next = head;
注意:由于使用了虚拟头节点,所以最终应该返回
return p->next;
。