給定一個連結串列的頭節點 head
,返回連結串列開始入環的第一個節點。 如果連結串列無環,則返回 null
。
如果連結串列中有某個節點,可以透過連續跟蹤 next
指標再次到達,則連結串列中存在環。 為了表示給定連結串列中的環,評測系統內部使用整數 pos
來表示連結串列尾連線到連結串列中的位置(索引從 0 開始)。如果 pos
是 -1
,則在該連結串列中沒有環。注意:pos
不作為引數進行傳遞,僅僅是為了標識連結串列的實際情況。
不允許修改 連結串列。
示例 1:
輸入:head = [3,2,0,-4], pos = 1 輸出:返回索引為 1 的連結串列節點 解釋:連結串列中有一個環,其尾部連線到第二個節點。
示例 2:
輸入:head = [1,2], pos = 0 輸出:返回索引為 0 的連結串列節點 解釋:連結串列中有一個環,其尾部連線到第一個節點。
示例 3:
輸入:head = [1], pos = -1 輸出:返回 null 解釋:連結串列中沒有環。
我們使用兩個指標, fast與 slow。它們起始都位於連結串列的頭部。隨後,slow 指標每次向後移動一個位置,而fast 指標向後移動兩個位置。如果連結串列中存在環,則 fast指標最終將再次與 slow指標在環中相遇。 如下圖所示,設連結串列中環外部分的長度為a。 slow指標進入環後,又走了b 的距離與 fast相遇。此時,fast指標已經走完了環的n 圈,因此它走過的總距離為a+n(b+c)+b=a+(n+1)b+nc。
根據題意,任意時刻,fast 指標走過的距離都為slow 指標的2倍。因此,我們有a+(n+1)b+nc=2(a+b)⟹a=c+(n−1)(b+c) 有了 a=c+(n−1)(b+c) 的等量關係,我們會發現:從相遇點到入環點的距離加上n−1 圈的環長,恰好等於從連結串列頭部到入環點的距離。 因此,當發現slow與fast相遇時,我們再額外使用一個指標ptr。起始,它指向連結串列頭部;隨後,它和slow 每次向後移動一個位置。最終,它們會在入環點相遇。
public class Solution { public ListNode detectCycle(ListNode head) { if (head == null) { return null; } ListNode slow = head, fast = head; while (fast != null) { slow = slow.next; if (fast.next != null) { fast = fast.next.next; } else { return null; } if (fast == slow) { ListNode ptr = head; while (ptr != slow) { ptr = ptr.next; slow = slow.next; } return ptr; } } return null; } }
時間複雜度:O(N),其中 N 為連結串列中節點的數目。在最初判斷快慢指標是否相遇時,slow 指標走過的距離不會超過連結串列的總長度;隨後尋找入環點時,走過的距離也不會超過連結串列的總長度。因此,總的執行時間為O(N)+O(N)=O(N)。
空間複雜度:O(1)。我們只使用了 slow , fast , ptr三個指標。