力扣Hot100-19 删除链表倒数第N个节点
全部刷题与学习记录
原题目
题目地址:19. 删除链表的倒数第 N 个结点
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
**进阶:**你能尝试使用一趟扫描实现吗?
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
输入:head = [1], n = 1
输出:[]
考查知识点
双指针、递归
自己的第一遍解法
直观的想法就是扫描两遍,第一遍计算链表长度size
,第二遍找到倒数第n+1个节点,也就是正数size-n-1处的节点,然后更改指向,越过要删除的节点
//扫描两遍,一遍确定链表长度,第二遍删除节点
class SolutionScan2 {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
if (head == nullptr) return nullptr;
//统计链表大小
int size = 0;
ListNode* curNode = head;
while (curNode != nullptr) {
size++;
curNode = curNode->next;
}
//找到倒数第n也就是正数size-n-1个节点,特例头结点
if (size == n) return head->next;
curNode = head;
int i = 1;
while (curNode != nullptr && i <= size - n - 1) {
curNode = curNode->next;
i++;
}
//删除该节点
curNode->next = curNode->next->next;
return head;
}
};
好的解法
题目中说最好是只扫描一遍,链表和数组类题目,一般都离不开双指针,尤其是链表的特殊性,还能够用递归来解。这里参考了公众号【数据结构与算法】的双指针和递归解法
使用双指针和递归就能做到只扫描一遍
双指针
倒数第n+1个节点和尾节点的距离一定是n,那么只要维护两个指针fast
和slow
,让他们之间距离是n,只要fast指向尾节点了,slow指向的必然就是倒数第n+1个节点
//双指针法,两指针先保持n的距离,fast指向尾节点的时候,slow指向的就是要删除节点的前一节点
class Solution_douPtr {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
if (head == nullptr) return nullptr;
ListNode* fast = head;
ListNode* slow = head;
//fast移动n步与slow拉开距离
for (int i = 0; i < n; ++i) {
fast = fast->next;
}
//fas为空节点,说明移动了list.size步,要删除的是头结点
if (fast == nullptr) return head->next;
//保持slow与fast间距同时移动,直到slow指向要删除节点的前一节点
while (fast->next != nullptr) {
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
return head;
}
};
递归
递归的方法就是直接从尾节点开始计算长度,当计算到倒数第n+1个节点的时候,就可以了
//递归
class Solution {
private:
//从尾节点开始倒数长度,直到找到倒数n+1节点
int length(ListNode* node, int n) {
if (node == nullptr)
return 0;
int pos = length(node->next, n) + 1;
if (pos == n + 1)
node->next = node->next->next;
return pos;
}
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
int pos = length(head, n);
if (pos == n)
return head->next;
return head;
}
};