方法:双指针
这个解法使用了两个指针——l
(慢指针)和r
(快指针),以及一个哑节点来简化头节点的处理。快指针先行走 n
步,然后慢指针和快指针一起移动,直到快指针到达链表末尾。此时,慢指针正好在倒数第 n+1
个节点处,可以进行删除操作。
解题步骤
-
初始化哑节点:创建一个哑节点
dummyHead
并将其next
指针指向head
。这一步是为了简化在头节点前删除节点的操作。 -
快慢指针定位:
- 将两个指针
l
和r
都指向dummyHead
。 - 将快指针
r
向前移动n
步,然后再向前移动一步,以保证它与慢指针l
之间有n
个节点。
- 将两个指针
-
同时移动两个指针:当快指针
r
不为空时,同时移动l
和r
直到r
指向链表末尾的nullptr
。此时,慢指针l
指向的是倒数第n+1
个节点。 -
删除节点:将慢指针
l
的next
指向l -> next -> next
,从而跳过了当前l -> next
节点,实现删除操作。 -
返回结果:由于
dummyHead
是哑节点,其next
指向的是真正的头节点。返回dummyHead -> next
即可。
代码分析
这个函数通过快慢指针技巧巧妙地找到了待删除节点的前一个节点。使用哑节点免除了头节点被删除的特殊处理,使代码更加简洁。不过需要注意的是,这个代码实现没有删除被移除的节点,实际应用中可能需要根据具体情况对其进行内存回收。
复杂度分析
- 时间复杂度:O(L),其中 L 是链表的长度,因为最坏情况下需要遍历整个链表。
- 空间复杂度:O(1),没有使用额外的空间。
Code
/**
* 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 *dummyHead = new ListNode(0);
dummyHead -> next = head;
ListNode *l = dummyHead;
ListNode *r = dummyHead;
while(n -- && r->next != NULL) r = r -> next;
r = r -> next;
while(r != NULL){
l = l -> next;
r = r -> next;
}
l -> next = l -> next -> next;
return dummyHead -> next;
}
};