Leetcode 19.
删除链表的倒数第 N 个结点
https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
最近在刷Leetcode,这道题给的题解让人耳目一新。
1.方法一
- 首先求长度,自己写一个函数,删除倒数第N个节点,就是删除正数第L-N+1个节点,L为长度,注意链表长度从1开始计数。这个不难想到,题解的亮点在于引入了哑节点,什么是哑节点,为什么要引入哑节点?
- https://blog.csdn.net/x55x5/article/details/82493185?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control
- https://blog.csdn.net/weixin_44388094/article/details/100929840
- 这两篇文章大佬讲得很透彻,尤其是第一篇大佬讲的,强推。总的来说哑节点它的 next 指针指向链表的头节点。这样一来,我们就不需要对头节点进行特殊的判断了,它的初始值就为NULL,但是它在堆中占有一定的空间,可以避免边界问题的处理,简化代码,例如在本题中,如果我们要删除节点 yy,我们需要知道节点 yy 的前驱节点 xx,并将 xx 的指针指向 yy 的后继节点。但由于头节点不存在前驱节点,因此我们需要在删除头节点时进行特殊判断。但如果我们添加了哑节点,那么头节点的前驱节点就是哑节点本身,此时我们就只需要考虑通用的情况即可。
/**
* 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:
int getLength(ListNode* head)
{
int length=0;
while(head)
{
head=head->next;
++length;
}
return length;
}
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy=new ListNode(0,head);//哑节点
int length=getLength(head);
ListNode* cur=dummy;//要进行遍历所用的节点
for(int i=1;i<length-n+1;i++)
{
cur=cur->next;
}
cur->next=cur->next->next;
ListNode* ans=dummy->next;
delete dummy;//释放空间
return ans;
}
};
2.方法二
快慢指针法:删除链表的倒数第 n 个结点,首先要确定倒数第 n 个节点的位置。
我们可以设定两个指针,分别为 slow 和 fast,刚开始都指向 head。
然后先让 fast 往前走 n 步,slow 指针不动,这时候两个指针的距离为 n。
再让 slow 和 fast 同时往前走(保持两者距离不变),直到 fast 指针到达结尾的位置。
这时候 slow 会停在待删除节点的前一个位置,让 slow->next = slow->next->next 即可。
但这里有一个需要注意的边界情况是:如果链表的长度是 L,而我们恰好要删除的是倒数第 L 个节点(删除头节点),这时候 fast 往前走 n 步之后会变为 null,此时我们只需要让 head = slow->next 即可删除。
```cpp
在这里插入代码片/**
* 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) {
if(head->next==nullptr)//样例二的测试
return NULL;
ListNode* fast=head;
ListNode* slow=head;
for(int i=1;i<=n;i++)
{
fast=fast->next;//注意是<=n而非<n可以自己纸上模拟一下
}
if(fast==nullptr)//边界情况 删除倒数第L个节点即为删除头节点 L代表链表长度
{
head=slow->next;//直接让head=slow->next即可
}
else
{
while(fast->next!=nullptr)
{
slow=slow->next;
fast=fast->next;
}
slow->next=slow->next->next;
}
return head;
}
};
3.方法三
方法三其实是用栈的方法实现,栈先进后出。弹出栈的第 n 个节点就是需要删除的节点,并且目前栈顶的节点就是待删除节点的前驱节点。如此,删除操作就变得十分方便。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy = new ListNode(0, head);
stack<ListNode*> stk;
ListNode* cur = dummy;
while (cur) {
stk.push(cur);
cur = cur->next;
}
for (int i = 0; i < n; ++i) {
stk.pop();
}
ListNode* prev = stk.top();
prev->next = prev->next->next;
ListNode* ans = dummy->next;
delete dummy;
return ans;
}
};//具体参考的官方题解。