Linked List Cycle II 是一道關於Linked List的經典算法題。給定一個Linked List,如果該Linked List中存在環,則返回環的起始節點。如果沒有環,則返回 null。
Python
方法一 :
利用 slow & fast pointer (slow 走一步,fast 走兩步),如果遇到就是有 cycle。
如何找出 cycle node 呢?
假設 cycle 長度為 m:(1)假設 slow 走 K 步,代表 fast 走了 2K 步,此時相遇了,代表了 fast 比 slow 多繞了迴圈至少一次,因此 (2K – K) = n * m
(2) 此時一個人從起點開始走,另外一個人從相遇點開始走(一次都走一步),會在 cycle node 相遇,為什麼?
原因:從起點走到相遇點是 K,而 K = n * m (迴圈的倍數),因此這兩個人至少一定會在剛剛的相遇點相遇。 而他們要能夠再相遇點相遇,則必定在 cycle node 就要遇到,才能夠一起走到相遇點!
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
slow = fast = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if fast == slow:
temp = head
while slow != temp :
slow = slow.next
temp = temp .next
return slow
return None
方法二 :
首先,使用快慢指針確定鍊錶是否有環,如果沒有環則返回 null。
如果有環,找到快慢指針相遇的位置。
令一個指針從鍊錶頭部出發,另一個指針從相遇位置出發。
兩個指針同時移動,每次移動一個節點,當它們相遇時,相遇點即為環的起始節點
def detectCycle(head: ListNode) -> ListNode:
# 第 1 步
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
# 第 2 步
break
if not fast or not fast.next:
# 第 3 步
return None
# 第 4 步
slow = head
while slow != fast:
slow = slow.next
fast = fast.next
return slow
C++
先使用快慢指針判斷鍊表是否有環。
如果有環,那麼我們需要找到環的起點。
我們設鍊錶頭節點到環起點的距離為 a,環起點到快慢指針第一次相遇點的距離為 b,快慢指針相遇點到環起點的距離為 c。根據快慢指針的速度關係可以得到:
快指針走過的距離 = 慢指針走過的距離 + n * 環長
快指針走過的距離 = 慢指針走過的距離 + 1 * 環長
經過計算可以得到:a = (n – 1) * 環長 + (環長 – c),即鍊錶頭節點到環起點的距離等於 n – 1 圈的環長加上環長與環起點到相遇點的距離之和。
在快慢指針第一次相遇後,讓一個指針從鍊錶頭節點出發,另一個指針從相遇點出發,兩個指針都以相同的速度前進。它們將會在環起點處相遇。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *slow = head, *fast = head;
while (fast != nullptr && fast->next != nullptr) {
slow = slow->next;
fast = fast->next->next;
if (slow == fast) { // 相遇了
ListNode *ptr = head;
while (ptr != slow) { // 找環起點
ptr = ptr->next;
slow = slow->next;
}
return ptr;
}
}
return nullptr; // 沒有環
}
};