此題位於子章節 Two Pointer Technique,所以我不用直覺想到的 memo 紀錄步驟的遞迴解法。
示範提到,使用兩個 Pointer,一個快一個慢,快的一次走兩步,慢的走一步。
兩個 Pointer 的起點都是 head,如果有循環,Fast Pointer 遲早會追上 Slow Pointer
class Solution:
def hasCycle(self, head: ListNode) -> bool:
if not head: return head
fastPointer, slowPointer = head, head
while 1:
# FastPointer go 2 steps forward
for _ in range(2):
if fastPointer.next:
fastPointer = fastPointer.next
if fastPointer == slowPointer:
return True
else:
return False
# SlowPointer go 1 steps forward
for _ in range(1):
slowPointer = slowPointer.next
另外一種用 memo 紀錄步驟的方法。
紀錄步驟是需要用到不會重複的值,如果只是 val,除非保證不會有相同 val,不然可能會出錯。
最保險起見的方法,是使用記憶體位置,作為是否曾經走過的依據。
幸好物件可以直接加入set,而物件在set中做為比較的數值是記憶體位置。
class Solution:
def hasCycle(self, head: ListNode) -> bool:
if not head: return head
memo = set()
while head.next:
if head in memo: return True
memo.add(head)
head = head.next
return False
我在想,如果要得加入一個有用的變數在裡面,到底要怎麼加?這才是勾起我的興致的發想。 想出來了,硬是要在裡面加入的 steps 紀錄步數。
class Solution:
def hasCycle(self, head: ListNode) -> bool:
if not head: return head
fastPointer, slowPointer = head, head
steps = 0
while fastPointer.next:
fastPointer, steps = fastPointer.next, steps+1
if fastPointer == slowPointer: return True
if steps%2==1: slowPointer = slowPointer.next
return False
接著看到 II,這次要回傳循環起點,如果用 memo 紀錄,這個問題只是將回傳False改成當前的節點。
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
if not head: return head
memo = set()
while head.next:
if head in memo: return head
else: memo.add(head)
head = head.next
return None
如果用快慢指針來解要怎麼辦?在兩者相遇之後,把慢指針移回原點,快慢指針每次都指往前一步,就會在循環起點相遇。
解釋:快2步,慢1步,當慢指針到了循環起點時,快指針在循環中也走了循環起點距離。而兩者的所剩距離是 循環長度 – 循環起點距離。
因此,慢指針往前「循環長度 – 循環起點距離」,兩者就會在循環內的 (循環長度 – 循環起點距離)位置上相遇。
此時把慢指針挪回起點,慢快指針同時走一步,前進 循環起點距離 步時,慢指針會來到 循環起點, 快指針會來到 循環長度 的位置,也是循環起點,兩者在此相遇。
![]() |
手稿示意圖 |
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
if not head: return head
FastPointer, SlowPointer = head, head
while 1:
# SlwoPointer go 1 step forward
SlowPointer = SlowPointer.next
# FastPointer go 2 steps forward
if FastPointer.next and FastPointer.next.next:
FastPointer = FastPointer.next.next
if FastPointer == SlowPointer: break
else:
return None
# FP SP meet in cycle(CycleLength - pos)
SlowPointer = head
while 1:
if FastPointer == SlowPointer: return FastPointer
SlowPointer = SlowPointer.next
FastPointer = FastPointer.next