给你一个链表,删除链表的倒数第 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) {}
* };
*/
class Solution {
int getlength(ListNode* head){
int length=0;
while(head){
++length;
head=head->next;
}
return length;
}
public:
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;
}
};
具体实现步骤如下:
-
定义一个长度计数变量length,并初始化为0。
-
使用while循环遍历链表,每遍历一个节点,length加1。
-
返回length值。
-
在removeNthFromEnd函数中,创建一个dummy节点,并将其next指针指向head。
-
调用getlength函数获取链表长度,保存在变量length中。
-
初始化cur指针指向dummy节点。
-
使用for循环遍历链表,每次循环cur指针向后移动一位,直到找到要删除的节点的前一个节点。
-
将要删除节点的前一个节点的next指针指向要删除节点的下一个节点,完成删除操作。
-
将dummy节点的next指针保存在变量ans中,作为结果链表的头节点。
-
释放dummy节点的内存。
-
返回结果链表的头节点。
总结: 该算法通过遍历链表两次的方式,找到要删除的节点的前一个节点,并进行删除操作。在第一次遍历中,计算链表的长度,然后计算要删除的节点在链表中的位置。在第二次遍历中,找到要删除节点的前一个节点,并将其next指针指向要删除节点的下一个节点,完成删除操作。返回结果链表的头节点。
解法二:栈
/**
* 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* 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;
}
};
具体思路如下:
-
首先定义一个辅助节点dummy,将其next指向head,以便处理删除头节点的情况。
-
创建一个栈stk,用于存储链表节点。
-
初始化cur指针指向dummy节点。
-
使用while循环遍历链表,将链表节点依次压入栈中。
-
循环结束后,栈中存储的节点就是链表节点的逆序。
-
弹出栈中的前n个节点,即为要删除的节点的前一个节点。
-
弹出的节点prev的next指针指向要删除节点的下一个节点,完成删除操作。
-
将dummy节点的next指针保存在变量ans中,作为结果链表的头节点。
-
释放dummy节点的内存。
-
返回结果链表的头节点。
总结: 该算法通过使用栈来逆序存储链表节点,并在弹出栈顶节点时找到要删除的节点的前一个节点,完成删除操作。返回结果链表的头节点。
解法三:快慢指针
/**
* 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* dummy=new ListNode(0,head),*first=head,*second=dummy;
for(int i=0;i<n;i++)
first=first->next;
while(first!=nullptr){
first=first->next;
second=second->next;
}
second->next=second->next->next;
ListNode* ans=dummy->next;
delete dummy;
return ans;
}
};
这段代码也是用于移除链表倒数第n个节点的,但是使用了双指针的思路。具体思路如下:
-
首先定义一个辅助节点dummy,将其next指向head,以便处理删除头节点的情况。
-
创建两个指针first和second,分别指向head和dummy节点。
-
使用for循环将first指针向前移动n个节点,此时first指针指向要删除的节点。
-
使用while循环,同时移动first和second指针,直到first指针指向链表的末尾(即first指针为nullptr)。
-
此时,second指针指向的节点为要删除的节点的前一个节点。
-
将second指针的next指针指向要删除节点的下一个节点,完成删除操作。
-
将dummy节点的next指针保存在变量ans中,作为结果链表的头节点。
-
释放dummy节点的内存。
-
返回结果链表的头节点。
总结: 该算法使用两个指针来定位要删除的节点的前一个节点,并进行删除操作。返回结果链表的头节点。