一、算法介紹
Floyd 龜兔賽跑算法(也稱為 Floyd 判圈算法或 Floyd 循環(huán)檢測算法)
是一種用于檢測鏈表中是否存在環(huán)的算法。如果鏈表上存在環(huán),那么在環(huán)上以不同速度前進的兩個指針必定會在某個時刻相遇。
1.判斷是否有環(huán)
龜一次走一步,兔子一次走兩步:
- 當兔子能走到終點時,不存在環(huán)
- 當兔子能追上龜時,存在環(huán)
2.尋找環(huán)入口
從它們第一次相遇開始,龜回到起點,兔子保持原位不變
- 龜和兔子一次都走一步
- 當再次相遇時,地點就是環(huán)的入口
設起點到環(huán)入口長度為步,環(huán)的長度為
步,那么
步的長度為環(huán)的入口處。(因為環(huán)的長度是y,一個y就是從起點到終點,n個y就是重疊了n次,還是在入口處)
第一次相遇龜和兔走的路程:
-
(必須是在繞環(huán)的過程中才能追上龜)
-
(
)
因為兔子的距離是烏龜?shù)膬杀叮ㄍ米右淮?步,烏龜一次1步),所以:
所以烏龜走的距離正好是整數(shù)倍的環(huán)長度,即。
根據(jù)前面環(huán)入口處的長度為,所以,在烏龜和兔子相遇之后,烏龜只需要再走
步,此時距離就為
,即環(huán)入口的長度。
此時,要計算出為具體為多少,可以讓兔子模擬烏龜(即每次只走一步),那么兔子需要前進
步走到環(huán)入口;讓烏龜回到起點,烏龜前進
步也能走到環(huán)入口,所以
的值就是他們相遇時候所走的步數(shù);即相遇的點就是環(huán)的入口。
二、Leetcode141.環(huán)形鏈表(判斷是否有環(huán))
判斷是否有環(huán):
使用兩個指針,一個快指針和一個慢指針,分別以不同的速度遍歷鏈表,兩個指針相遇代表有環(huán),否則無環(huán)。
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) {
return false;
}
ListNode fast = head;
ListNode slow = head;
while (true) {
fast = fast.next.next;
slow = slow.next;
// 有環(huán)
if (fast == slow) {
return true;
}
// 無環(huán)
if (fast == null || fast.next == null) {
return false;
}
}
}
三、Leetcode142.環(huán)形鏈表II(尋找環(huán)入口)
尋找環(huán)的入口:
在第一次相遇后,將 slow 指針重新指向鏈表頭節(jié)點,然后 slow 和 fast 指針都以相同的速度(每次移動一步)前進,當它們再次相遇時,相遇點即為環(huán)的入口點。
public ListNode detectCycle(ListNode head) {
if (head == null || head.next == null) {
return null;
}
ListNode fast = head;
ListNode slow = head;
while (true) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
// 相遇 讓slow回到起點
slow = head;
while (fast != slow) {
// 分別前進一步
fast = fast.next;
slow = slow.next;
}
return slow;
}
if (fast == null || fast.next == null) {
return null;
}
}
}