一、题目
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
输出:[]
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list
二、思路及解法
书上直接给出了快慢指针法,对于这一类窗口问题快慢指针确实是最优的,但是我首先想到的笨办法倒也能做对,简单说一下
1.硬反转
删除倒数嘛,整个反转删除正数第n个,然后再转回去就好了(正好上一节是反转)
方法很简单粗暴,没什么技术含量,直接上代码
ListNode* reverse(ListNode* head)
{
ListNode* tmp;
ListNode* pre = nullptr, * cur = head;
while (cur != nullptr)
{
tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
ListNode* removeNthFromEnd(ListNode* head, int n)
{
head = reverse(head);
ListNode* illus_head = new ListNode(0);
illus_head->next = head;
ListNode* cur = illus_head;
while (--n)
{
cur = cur->next;
}
ListNode* temp = cur->next;
cur->next = cur->next->next;
delete temp;
illus_head->next = reverse(illus_head->next);
return illus_head->next;
}
2.快慢指针法
用两个指针,因为要删倒数第n个,所以需要确定距尾节点n个位置的节点。而删除操作要求我们确定其父节点位置,所以正好用两个指针卡出一个长度为n的区间
当然,删除还是要加个虚拟头,方便处理
先让fast指针跑出n个位置,然后两个一起跑,直到fast跑到尾节点,此时slow就对应目标节点的父节点。
完整代码如下
ListNode* fastslow(ListNode* head, int n)
{
ListNode* pre, * cur;
ListNode* dhead = new ListNode(0);
dhead->next = head;
cur = pre = dhead;
while (n--&&cur!=nullptr)
{
cur = cur->next;
}
while (cur->next!= nullptr)
{
cur = cur->next;
pre = pre->next;
}
ListNode* tmp;
tmp = pre->next;
pre->next = pre->next->next;
delete tmp;
tmp = nullptr;
return dhead->next;
}
3.注意事项
上述两个方法都有一个小雷区,即返回值的选择。我最开始写的时候直接返回的是head,但两种方法都错在了同一个case,即head = [1], n = 1
如果采用虚拟头,根据上述方法删除过后,这一case中的head已经被释放掉了(delete tmp),再返回就不知道返回了个什么东西。所以安全保险的方法是直接用illus_head->next做返回值,在上述特殊情况中,这个值一定是null