目录
一、「Floyd 判圈算法」(又称龟兔赛跑算法)
- 假想「乌龟」和「兔子」在链表上移动,「兔子」跑得快,「乌龟」跑得慢。当「乌龟」和「兔子」从链表上的同一个节点开始移动时,如果该链表中没有环,那么「兔子」将一直处于「乌龟」的前方;如果该链表中有环,那么「兔子」会先于「乌龟」进入环,并且一直在环内移动。等到「乌龟」进入环时,由于「兔子」的速度快,它一定会在某个时刻与乌龟相遇,即套了「乌龟」若干圈。
- 我们可以将上面的情景演变成快慢指针算法。该算法可以解决有关链表带环的问题。具体地,我们定义两个指针,一快一慢。慢指针每次只移动一步,而快指针每次移动两步。初始时,慢指针在位置 head,而快指针在位置 head.next。这样一来,如果在移动的过程中,快指针反过来追上慢指针,就说明该链表为环形链表。否则快指针将到达链表尾部,该链表不为环形链表。
二、龟兔赛跑算法的实现
2.1、第一阶段
我们定义两个指针一个slow,一个fast,使用slow来模拟乌龟,使用fast来模拟兔子fast每次走两步,slow每次走一步,它们两个在环内发生追逐,最终二者一定是会相遇的。
下面来思考两个问题:
1、二者为什么一定会相遇,如何进行证明?
- 当快指针走两步,慢指针走一步的时候一定会相遇的。这其中的一个关键点就在于他们二者的相对速度。我们做这样的假设,从链表的头节点开始,快指针和慢指针同时出发,起点到达环入口的距离设为L,环的周长设为C,快指针走到环入口时,快指针在环内走过的距离设为K。我们很容易就可以想到二者之间的差距C-K如果可以被快慢指针的相对速度进行整除,那么就一定可以相遇。
- 快指针每次走两步,慢指针每次走一步,二者的相对速度为1,而1可以被任何数进行整除,这就说明了二者一定是可以相遇的。
2、如何寻找环的入口?
- 由于只要是二者之间的距离可以被相对速度整除,那么就一定是可以相遇的。我们保持快指针的位置不变,慢指针回到起点让他们二者重新每次都走一步,那么接下来的相遇点就是环的入口。
- 证明:下面我们使用数学知识进行一下模拟,我们知道二者之间的速度关系是2:1那么相遇的时候会有这样的关系存在着2(L+ K) = L + K + nC(这里的n是有L确定的,当满足速度比为2:1的关系时,n应该等于1),化简得:L + K = nC,这里的n等于1,所以L = C - K,而重相遇点到环入口的距离正好是C - K,那么二者下一次的相遇点一定是在环的入口处。
3、总结
- 当按照1:3的规律进行绕环时,相对速度变成了2,这样只有K为偶数才可以追上,但是真的是这样吗,就按照上面的图为例,此时的K奇数,第一圈肯定是没有追上的,这意味着什么呢?就是要进入下一轮的追逐了,但是下一轮慢指针就进入了环内,而快指针又正好和慢指针错过,他们之间的距离就变成了C-1,我们只是需要满足C-1为偶数就一定是可以追上的。
- 做出如下总结:当C为偶数,K为奇数时是一定追不上的,不可能相遇。
2.2、第二阶段
接下来,我们来推广到一般:我们现在已经知道了当快指针的速度为2慢指针的速度为1时他们满足上面的规律,那么当快指针为3,4,5....n的时候还满不满足上面的规律呢?
答:我们使用数学公式进行推导分析,3L = L + nC + C - K,化简可得2L = (n +1)C - K2L一定是为偶数的,只有偶数-偶数等于偶数,奇数-奇数等于偶数,(n + 1)C 和 K要么同时为奇数要么同时为偶数,我们上面的结论是K如果为偶数,那么第一圈就会相遇,如果K为奇数的话那么(n + 1)C也一定要为奇数,即(n + 1)和C都要为奇数,这样才能保证上面的式子是成立的。所以根本不会存在C是偶数,K是奇数的情况的。
总结,当快指针和慢指针的速度比满足n:1的时候,就一定会相遇,只不过,当K为偶数的时候会在第一圈相遇,当K为奇数的时候会在第二圈相遇,且相对速度为偶数的时候会在第二圈相遇,不建议使用1:3...n的情况,我们一般都是使用1:2的速度比进行的。
三、代码实现
下面我们利用这个龟兔赛跑算法来解决一下判断链表是否带环这个问题。
class Solution {
public:
bool hasCycle(ListNode* head) {
if (head == nullptr || head->next == nullptr) {
return false;
}
ListNode* slow = head;
ListNode* fast = head->next;
while (slow != fast) {
if (fast == nullptr || fast->next == nullptr) {
return false;
}
slow = slow->next;
fast = fast->next->next;
}
return true;
}
};