【演算法分析】如何理解快慢指標?判斷linked list中是否有環、找到環的起始節點位置。以Leetcode 141. Linked List Cycle, 142. Linked List Cycle II 為例Python實現
阿新 • • 發佈:2018-12-17
快慢指標簡述
快慢指標經常用於連結串列(linked list)中環(Cycle)相關的問題。
- 快指標(fast pointer)和慢指標(slow pointer)都從連結串列的head出發。
- slow pointer每次移動一格,而快指標每次移動兩格。
- 如果快慢指標能相遇,則證明連結串列中有環;否則沒有。
快慢指標的具體程式碼(C++, Python, Java版本)可以參考這個連結。
LeetCode中對應題目分別是:
問題詳解 —— 為什麼如果有環,則快慢指標必定會相遇?
我們假設以下變數:
\(L_1\):起始節點(head node)到環起始節點(entry node)的距離。 \(C\)
假設我們的慢指標移動了\(x\)步,那麼快指標就移動了\(2x\)步。 那麼必定有\[(x-L_1)\% C= (2x-L_1) \% C \]\[(2x-x)\% C = 0\]\[x\%C=0\] 以上三個式子步步可逆,由於\(C\)是給定的fixed value,而\(x\)每步都在上升,因此必定有一個\(x=wC(w\in {N})\)使得他們相遇。並且有\(L_1 \le x\)所以必有\(x=\)⌈\(\frac{L_1}{C}\)⌉\(C\)為他們第一次相遇的地點。因此有\(x< L_1 + C\) where \(x=\)⌈\(\frac{L_1}{C}\)⌉\(C\)
問題詳解 —— 如何找到環的起始節點?
我們再增加一些變數: \(L_2\): 環起始節點(entry node)到快慢指標相遇節點的距離。 \(k\): 慢指標和快指標相遇的時候,慢指標走了的距離。 注意到,因為快指標走了的距離總是慢指標走了的距離的兩倍,因此\(2k\)是慢指標和快指標相遇的時候,快指標走了的距離。
由慢指標可以得出\[L_1+L_2=k\]由快指標可以得出,其中n是快指標已經走過的圈數\[L_1+nC+L_2 = 2k\]結合上述兩個式子,我們可以得出\[nC=k\] 當慢指標在遇到了快指標之後,慢指標又馬上移動了,那麼慢指標需要移動\(p\)
LeetCode 對應程式碼
判斷linked list中是否有環(141. Linked List Cycle)
這個演算法的時間複雜度是O(n)
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
if head is None:
return False
slow = head
fast = head
while(fast.next and fast.next.next):
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
找到環的起始節點(entry node)位置(142. Linked List Cycle II)
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
if head is None:
return None
slow, fast, new_node = head, head, head
while(fast.next and fast.next.next):
slow = slow.next
fast = fast.next.next
if slow == fast:
while slow != new_node:
new_node = new_node.next
slow = slow.next
return new_node
return None