给你一个链表,删除链表的倒数第 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
思路:两次遍历 |
- 第一次遍历求出链表长度。
- 第二次遍历删掉指定结点。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy=new ListNode(-1); //头结点可能会变动就要建立虚拟头结点
dummy->next=head;
ListNode* first=dummy;
int k=0;
for(auto p=dummy;p;p=p->next){ //第一遍遍历求出总结点数k
k++;
}
for(int i=0;i<k-n-1;i++){ //跳一步能跳到第二个结点,所以总个数为5,n=2时,跳2步即可
first=first->next;
}
first->next=first->next->next;
return dummy->next;
}
};
复杂度分析:
- 时间复杂度:遍历两次链表,故空间复杂度为 O(L)。
- 空间复杂度:仅需要定义常数个指针变量,故空间复杂度为 O(1)。
进阶思路:一次遍历 |
- 在头部之前添加保护结点。
- 设置两个指针 first 和 second,均指向保护结点。
- first 指针先向后移动 n+1 个结点。
- 然后 first 和 second 指针同时向后移动,直到 first 指针指向空,此时 second 结点指向的下一个结点需要删除。
- 解释:始终保持两个指针之间间隔 n 个结点,在 first 到达终点时,second 的下一个结点就是从结尾数第 n 个 结点。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy=new ListNode(-1);
dummy->next=head;
ListNode* first=dummy;
ListNode* second=dummy;
for(int i=0;i<=n;i++){
first=first->next;
}
while(first!=NULL){
first=first->next;
second=second->next;
}
second->next=second->next->next;
return dummy->next;
}
};
复杂度分析:
- 时间复杂度:遍历一次链表,故空间复杂度为 O(L)。
- 空间复杂度:仅需要定义常数个指针变量,故空间复杂度为 O(1)。