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