过去刷LeetCode题推动那个工人指针跑的循环大多数都是while循环。因为我们在C语言刚接触循环这个概念的时候就学过,for一般都用于作用的结构大小较为明显,循环次数确定的场景下。而while之所以称为条件循环就是因为其没有固定的循环次数限制。一般在链表中,当我们的工人指针来到链表尽头的时候会结束循环,所以我们一般会使用while ( worker != nullptr )这样的一个语句来判断链表是否遍历完毕。那么今天这道题中,for循环在链表中,是不是能派上什么用场呢?我们先来读题
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5. 说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?
思路一:快慢指针
关于快慢指针,我们之前已经在找链表中点的题目中有所介绍,我们在那一期便指出了快慢指针的速度是可以调配的,并不是只能像我们在找中点时规定的v(fast_pointer) = 2v(slow_pointer)的。我们的目标只是要让两个指针在链表中有相对位置上的差距。这道题中,需要返回倒数第N个结点,我们想这样一个生活中的场景:班里有6个学生,在一次考试结束后老师根据成绩进行了排名。小宝考了最后一名(6th),小波说自己比小宝高3名 -> 小波的名次为(3rd),倒数第3名不是小波,而是小刚(4th)。即比倒数第一高了3名是倒数第4,而不是倒数第3,注意第这个字眼! 明白了这个道理再来看这道题。同样,当我们需要知道这个班级里某个同学的名次,都可以以小明为参照物,他只要说出自己的名次比小明高多少就可以了。那么推广到这道题中,我要知道倒数第N个结点是谁,我就先派一个工人指针先到链表的最后,当他到达链表最后的时候,那么要求后出发的工人指针所在的位置就是倒数第N个结点。我们这个后出发的工人指针与先出发的工人指针的相对位置差就是N,所以,呼应了我上方所讲的for循环在链表中的应用,我们确定先出发的这个工人指针要拉开N个身位,这也就确定了循环的次数,用for循环真的是再适合不过了。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *fast = head, *slow = head;
// 先让fast指针走n个单位
for ( int i = 0; i < n; ++i )
fast = fast->next;
// 题目中保证了N一定有效,所以无须担心我们的fast会出现先循环过了N步后就出现了fast不存在的情况
/* 当fast走到了尽头,成为了倒数第一,就说明此时的N为链表的总长度,也就是说倒数第N个元素也就是链表的第一个元素,于是我们只需要返回第一个链表结点之后的部分就可以了(这里并没有设置dummyHead,因此head就是第一个结点) */
if ( !fast )
return head->next;
// 模版:while ( ... )
while ( fast->next ){
fast = fast->next;
slow = slow->next;
}
// 删除操作一笔带过
slow->next = slow->next->next;
return head;
}
};
也可以直接在for循环时就直接判断fast下一步是否合法,这甚至规避了N不合法程序报错的风险。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *fast = head, *slow = head;
for ( int i = 0; i < n; ++i ) {
// 如果fast下一步不会走出街区,就继续走
if ( fast->next )
fast = fast->next;
// 一旦要走出街区了
else
return head->next;
}
while ( fast->next ){
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
return head;
}
};
这种思路下我没私自加这个头结点就是想让大家感受一下这个双指针怎么体现出倒数这个概念的,加头结点也可以做,而且我依然笃定的告诉你,这对新手来说,是个好习惯。这就是一次遍历的算法o!
总结一下
还是一个快慢指针的思想,有一个误区要走出来,不是说一定要某个指针的速度快于另一个指针,只要两个指针在相对位置上能有所距离,就可以使用快慢指针。
博客讲解过的Linked-List类的问题的代码均放在我单独为写链表专栏的博客的GitHub里
https://github.com/18260036169/LeetCode-LinkedList