力扣日记:【链表篇】删除链表的倒数第N个节点
日期:2023.8.16
参考:代码随想录、力扣
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
进阶:你能尝试使用一趟扫描实现吗?
题解
/**
* 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) {}
* };
*/
#define SOLUTION 2
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
#if SOLUTION == 1 // 遍历+check(时间复杂度:O(sz*n),空间复杂度:O(1))
ListNode* dummyHead = new ListNode(0, head);
ListNode* cur = dummyHead;
while (!check(cur, n)) { // 还没到倒数第n个节点
cur = cur->next;
}
// 到达倒数第n个节点
// 删除
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
tmp = nullptr;
return dummyHead->next; // 注意头节点被删除的情形
#elif SOLUTION == 2 // 先测长度,再遍历(时间复杂度:O(sz+sz),空间复杂度:O(1))
ListNode* dummyHead = new ListNode(0, head);
ListNode* cur = dummyHead;
int size = 0;
while (cur->next != nullptr) {
size++;
cur = cur->next;
}
cur = dummyHead;
int index = size - n;
// 遍历到倒数第n个节点
while (index--) {
cur = cur->next;
}
// 删除
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
tmp = nullptr;
return dummyHead->next;
#elif SOLUTION == 3 // 双指针(时间复杂度:O(sz),空间复杂度:O(1))
ListNode* dummyHead = new ListNode(0, head);
ListNode* fast = dummyHead;
ListNode* slow = dummyHead;
// 首先移动 fast n+1步
while (n-- + 1) {
fast = fast->next;
}
// 再同时移动fast和slow至fast为null
while (fast != nullptr) {
slow = slow->next;
fast = fast->next;
}
// 删除slow的下一个节点
ListNode* tmp = slow->next;
slow->next = slow->next->next;
delete tmp;
tmp = nullptr;
return dummyHead->next;
#endif
}
bool check (ListNode* cur, int n) {
ListNode* check = cur->next;
while(n-- != 0) {
check = check->next;
}
if (check == nullptr) {
return true;
}
return false;
}
};
执行结果
- 遍历+check
状态:通过
时间复杂度:O(sz*n)
空间复杂度:O(1)
- 先测长度,再遍历
状态:通过
时间复杂度:O(sz+sz)
空间复杂度:O(1)
- 双指针法
状态:通过
时间复杂度:O(sz)
空间复杂度:O(1)
思路总结
- 第一种解法:是在遍历的过程中就判断是否是倒数第n个节点。
- 第二种解法:则是先测出链表的长度,再根据倒数第n个节点得到对应下标index,再遍历删除。
- 还有双指针法(又没想到)!!!要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。再删掉slow所指向的节点即可。
- 注意在实际操作中,让fast移动n+1步,使得slow指向待删除节点的前一个节点,以方便删除。
- 注意头节点被删除的情形,所以最后的返回值需为
dummyHead->next。
本文介绍了三种方法解决链表删除倒数第N个节点的问题:遍历+检查、预估长度后遍历以及双指针法。分别分析了它们的时间复杂度和空间复杂度,双指针法是最优解,时间复杂度为O(sz)且空间复杂度为O(1)。





