写在前面
好久没有重新刷题了,最近突然觉得自己代码水平急速下降,别的东西做的多了,因而想来这里刷刷题目,希望回复一下手感。
题目描述
给定一个链表,删除链表的倒数第 n
个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?
解题思考
刚开始做这道题的时候苦思冥想,怎么样能够一次遍历就找到倒数第n个节点呢?我始终觉得是不可能的。后来我发现一个指针确实不能完成这样的事情,那两个指针呢?两个指针中间相距的间隔为n,不就可以了吗?
说干就干,我当即创建了两个指针,一个指针ptr
指向遍历过的最后一个节点,另一个指针delayN
指向ptr
的前n
个节点。这样,我们就能直接通过delayN
指针找到倒数第n
个节点了!
然而,实际操作和理论的思索还是有一些区别的,因为我发现,由于要删除节点,我们需要锚定delayN
在倒数n+1
位置的节点,那么这时候会出现两种可能:
- 倒数第
n
个节点实际上是头节点; - 倒数第
n
个节点不是头节点。
例如:1->2->3
这样一个链表,1
和3
之间的距离是2,如果n = 3
那么delayN
和ptr
的距离就必须是n = 3
,这显然是不可能的。想解决这个问题,那么两个指针之间的距离则会有两种可能:
- 指针间的距离是
n-1
; - 指针间的距离是
n
。
接下来的思路就清晰了,将ptr
锚定到最后一个节点,并在ptr
后移的时候判断指针之间的距离,如果distance = n
,则两个指针同时后移,如果distance < n
则只有ptr
后移。这样做之所以成立是因为题目里告诉我们n
必然有效,所以我可以放心大胆的移。
因为n
必然有效,因此当ptr
移到最后一个节点的时候,如果distance < n
那必然是删除头节点,我们只需要返回头节点的后一个节点了,它有两种可能:
NULL
(链表长度为1)head->next
如果distance = n
,就正常删除delay->next
,并返回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) {
ListNode* ptr = head;
ListNode* delayN = head;
int count = 0;
while(ptr->next != NULL){ // length > 1
if(count < n)
count++;
else
delayN = delayN->next;
ptr = ptr->next;
}
count == n ? delayN->next = delayN->next->next : head = head->next;
return head;
}
};
总结
最终结果:
执行用时:
0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:
6.1 MB, 在所有 C++ 提交中击败了100.00%的用户
代码比较简洁,因为我确实不太喜欢看到太多的if else
分支条件语句,因此能用三目运算符的就用了。看起来舒服一点。