【LeetCode-链表】删除链表的倒数第N个节点
题目描述
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?
题目链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
思路1
链表倒数第 n 个节点就是正数的第 len-n 个节点,其中 len 为链表长度。所以,先遍历链表一遍求长度 len,然后在遍历到链表的第 len-n 个节点前执行删除操作。需要注意的是,由于删除的可能是链表的第一个节点,所以需要使用一个新的链表头(哑结点)指向原来的链表。代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
if(head==nullptr) return nullptr;
int len = getLen(head);
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* curNode = dummyHead;
for(int i=0; i<len-n; i++){ // 循环 len-n 次,得到原链表中第 len-n-1 个节点(编号从 1 开始)
curNode = curNode->next;
}
curNode->next = curNode->next->next;
return dummyHead->next;
}
int getLen(ListNode* head){
int len = 0;
while(head!=nullptr){
len++;
head = head->next;
}
return len;
}
};
- 时间复杂度:O(n)
- 空间复杂度:O(1)
思路2
使用两个指针 p1, p2. p1 和 p2 初始都指向哑结点 dummyHead,然后 p2 先走 n 步,然后 p1 和 p2 共同往后走,当 p2->next==nullptr 时停止移动两个指针,此时 p1->next 就是倒数第 n 个节点。代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
if(head==nullptr) return nullptr;
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* p1 = dummyHead;
ListNode* p2 = dummyHead;
for(int i=0; i<n; i++) p2 = p2->next;
while(p2->next!=nullptr){
p1 = p1->next;
p2 = p2->next;
}
p1->next = p1->next->next;
return dummyHead->next;
}
};
- 时间复杂度:O(n)
- 空间复杂度:O(1)
总结
这个问题的难点在于不好确定应该移动几步,例如思路 2 中,p2 是先移动 n 步,还是 n-1 步或者 n+1 步。遇到这种问题,可以在纸上画出来分析一下,可以很快得出答案。