给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2. 当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?
思路:
这是一道中等难度的题目。如果可以两次遍历链表的话,最简单的方式就是通过第一次遍历记录链表表长,推算出倒数第 n 个节点的序号,然后通过第二次遍历删掉它即可。
那么如何使用一趟扫描实现这个目的呢?还是可以使用双指针技巧:让快指针 fastCursor 先走n个节点,然后慢指 slowCursor 和快指针 fastCursor 再同时出发,且均在每次循环时前进一个节点。这样就可以使得快指针完成链表遍历时,慢指针刚好停在倒数第n个节点上,这时删去该节点即可。
但事实是我们没有办法直接删去这个节点而同时保持链表的连续,我们必须要停在倒数第n个节点的父节点上。这里有个小技巧:新建一个val = -1的伪头节点dummyHead,通过这个技巧也能保证逻辑一致,且删去原本的头节点而不必单独判断处理。
AC代码:
//扫描两遍的做法
/**
* 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 NodeCounter = 0;
ListNode* cursor = head;
while(cursor)
{
NodeCounter += 1;
cursor = cursor->next;
}
int LastNode = NodeCounter - n;
cursor = head;
if(LastNode == 0)
{
ListNode* cursorD = cursor;
head = cursor->next;
delete cursorD;
}
else
{
while(LastNode > 1 && cursor != nullptr)
{
LastNode--;
cursor = cursor->next;
}
ListNode* cursorD = cursor->next;
cursor->next = cursor->next->next;
delete cursorD;
}
return head;
}
};
//扫描一遍的做法
/**
* 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 || head->next == nullptr)
return nullptr;
//新建一个头节点
ListNode dummyHead(-1);
dummyHead.next = head;
ListNode *slowCursor = &dummyHead, *fastCursor = &dummyHead;
//先走 n+1 个节点
while(fastCursor != nullptr && n > 0)
{
fastCursor = fastCursor->next;
n--;
}
//找到倒数第n+1个节点
while(fastCursor->next != nullptr)
{
slowCursor = slowCursor->next;
fastCursor = fastCursor->next;
}
//return slowCursor;
//删除该节点
ListNode* tempNode = slowCursor->next;
slowCursor->next = tempNode->next;
delete tempNode;
return dummyHead.next;
}
};
Published by