在刷LeetCode時,我們經常用到快慢指針。那麼它背後有怎樣的數學原理呢,今天就給大家獻醜了。
龜兔賽跑簡述
Floyd判圈算法(Floyd Cycle Detection Algorithm),又稱龜兔賽跑算法(Tortoise and Hare Algorithm),是一個可以在有限狀態機、迭代函數或者鏈表上判斷是否存在環,以及判斷環的起點與長度的算法。
原理
如圖,A爲出發點,B爲環的入口點。快慢指針都從A點出發,其中快指針的速度是慢指針的2倍。
判斷是否有環
如果存在環,快慢指針必定相遇;否則,慢指針走到結尾還未相遇則不存在環。
求環的長度
假設兩者在C點第一次相遇。再讓兩指針繼續前進,下次相遇時快指針比慢指針多走了一圈。
求環的起點
假設AB的長度爲m,BC的長度爲n,環的周長爲p。在相遇時,我們假設慢指針移動的距離爲s,有:
s = m + n + ap
而快指針的移動距離爲:
2s = m + n + bp
兩式相減有:
s = (b - a)p
也就是說慢指針走過的距離爲環長的整數倍。
在第一次相遇後,讓慢指針從C點出發,另一個指針從A點出發,都以慢指針的速度移動。則它們必定在B點相遇。因爲當從A點出發的指針走過m的距離到達B點時,慢指針已經走過了s+m的距離(也是從A點出發)。其中S爲環長的整數倍,那麼慢指針也必定再B點,所以相遇點爲環的入口點B點。
應用
LeetCode 142題
這是求環形鏈表的入口,代碼如下:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
if not head:
return None
slow, fast = head, head.next
while slow != fast:
slow = slow.next if slow else None
fast = fast.next.next if fast and fast.next else None
if not slow:
return None
entry = head
slow = slow.next
while entry != slow:
entry = entry.next
slow = slow.next
return entry
LeetCode 287
這是數組形式的尋找起點:
class Solution:
def findDuplicate(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if len(nums) <= 0:
return -1
slow = nums[0]
fast = nums[nums[0]]
while slow != fast:
slow = nums[slow]
fast = nums[nums[fast]]
entry = nums[0]
slow = nums[slow]
while slow != entry:
entry = nums[entry]
slow = nums[slow]
return entry