难度中等1852
给你一个链表,删除链表的倒数第 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
进阶:你能尝试使用一趟扫描实现吗?
我用的方法:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyNode = new ListNode(0);
dummyNode -> next = head;
ListNode* cur = dummyNode;
int count = 0;
while(cur){
cur = cur -> next;
count++;
}
ListNode* temp;
int num = count - n - 1;
cur = dummyNode;
while(num--){
cur = cur -> next;
}
temp = cur -> next;
cur -> next = cur -> next -> next;
delete temp;
return dummyNode -> next;
}
};
显然没有用到什么技巧,而是直接遍历链表得到链表中有多少个元素,然后循环找到要删除的节点再进行删除操作,这样的话效率会很低。
看了别人的代码后 删除链表的倒数第N个节点,发现这是一道双指针的经典应用,这应该就是进阶要求的方法,用一趟扫描实现。
如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。
分为如下几步:
-
定义fast指针和slow指针,初始值为虚拟头结点,如图:
-
fast首先走n + 1步 ,为什么是n+1呢,因为只有这样同时移动的时候slow才能指向删除节点的上一个节点(方便做删除操作),如图:
-
fast和slow同时移动,直到fast指向末尾,如题:
-
删除slow指向的下一个节点,如图:
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* slow = dummyHead;
ListNode* fast = dummyHead;
while(n-- && fast != NULL) {
fast = fast->next;
}
fast = fast->next; // fast再提前走一步,因为需要让slow指向删除节点的上一个节点
while (fast != NULL) {
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
return dummyHead->next;
}
};
可以很直观地看出,用双指针的方法比遍历后再查找删除效率高多了。
我的理解:使用快慢指针(fast=slow+n+1),快指针和慢指针同时移动,当fast移动到链表末尾时,slow停在要删除地节点前。
解决链表的题时,主要是要捋清指针的指向,不要被绕晕了。