給你一個連結串列的頭節點 head
,判斷連結串列中是否有環。
如果連結串列中有某個節點,可以透過連續跟蹤 next
指標再次到達,則連結串列中存在環。 為了表示給定連結串列中的環,評測系統內部使用整數 pos
來表示連結串列尾連線到連結串列中的位置(索引從 0 開始)。注意:pos
不作為引數進行傳遞 。僅僅是為了標識連結串列的實際情況。
如果連結串列中存在環 ,則返回 true
。 否則,返回 false
。
示例 1:
輸入:head = [3,2,0,-4], pos = 1 輸出:true 解釋:連結串列中有一個環,其尾部連線到第二個節點。
示例 2:
輸入:head = [1,2], pos = 0 輸出:true 解釋:連結串列中有一個環,其尾部連線到第一個節點。
示例 3:
輸入:head = [1], pos = -1 輸出:false 解釋:連結串列中沒有環。
提示:
- 連結串列中節點的數目範圍是
[0, 104]
-105 <= Node.val <= 105
pos
為-1
或者連結串列中的一個 有效索引 。
進階:你能用 O(1)
(即,常量)記憶體解決此問題嗎?
思路
快慢指標,倆指標從頭節點出發,一個快指標每次走兩步,一個慢指標每次走一步,如果連結串列存在環路,那麼快指標會追上慢指標
/** * Definition for singly-linked list. * class ListNode { * int val; * ListNode next; * ListNode(int x) { * val = x; * next = null; * } * } */ public class Solution { public boolean hasCycle(ListNode head) { if(head == null || head.next == null || head.next.next == null) return false; ListNode slow = head, fast = head; while(fast != null && fast.next!=null){ slow = slow.next; fast = fast.next.next; if(slow == fast) return true; } return false; } }
證明快慢指標演算法(也被稱為弗洛伊德的龜兔賽跑演算法)的正確性,可以透過數學歸納法來完成。下面是證明的邏輯:
-
定義:假設連結串列由非環部分和環部分組成。設非環部分的長度為 𝑎a 個節點,環的長度為 𝑏b 個節點。
-
快指標進入環:當快指標(
fast
)進入環時,慢指標(slow
)可能還在非環部分,或者已經在環內。 -
快慢指標相遇:當快指標在環內移動時,慢指標也在環內移動,但由於快指標的速度是慢指標的兩倍,它們最終會在環內的某個點相遇。
-
證明相遇:假設在快指標進入環之前,快慢指標已經移動了 𝑘k 步(𝑘<𝑎k<a)。此時,快指標開始在環內移動,而慢指標繼續在非環部分移動直到它也進入環。當慢指標進入環時,快指標已經在環內移動了 𝑘k 步。
-
環內相遇:在環內,快指標每走 𝑏b 步就會回到起點,而慢指標需要走 2𝑏2b 步。因此,每當快指標繞環一週,慢指標只繞了半周。這意味著,每過 𝑏b 步,快指標就會相對於慢指標多走 𝑏b 步。由於快指標和慢指標之間的初始距離是 𝑘k 步,經過 𝑘𝑏bk 輪之後,快指標將追上慢指標。
-
特殊情況:如果連結串列只有一個節點,或者沒有節點,快慢指標會在第一步就相遇,這符合我們的預期。
-
沒有環的情況:如果連結串列中沒有環,快指標會在到達連結串列末尾時停止,而慢指標會在快指標之前停止,因此它們永遠不會相遇。