Floyd's Tortoise and Hare & 環檢測算法

算法推導

image

hare的移動速度是tortoise的 2 倍,
設起始點到環的入口的距離是T,環的長度是C
tortoise第一次走到環的入口entry point時,我們假設這是tortoisehare之間的在環上的距離是r
start point開始出發到tortoise第一次走到環的入口時,hare移動的距離是 T + r + k*C,k >= 0
又因為,hare移動的速度是tortoise的兩倍,且這時tortoise移動的距離是T,所以hare移動的距離是 2T。
得到等式 A T + r + k*C = 2T,k >= 0
簡化得到等式 B r + k*C = T,k >= 0

[圖片上傳失敗...(image-1940ba-1559799507418)]
當 tortoise 第一次走到環的入口entry point時,而這時tortoisehare之間的距離是 r,
那么如果tortoise現在就不繼續移動的話,hare還需要往前走C-r才能追上tortoise
但是hare在往前追趕tortoise的時候,tortoise也在移動,而hare的移動速度是tortoise的兩倍,
所以hare可以追上tortoise,并且需要往前走2*(C-r)才能追上tortoise

image

hare移動了2*(C-r)的距離追上tortoise的時候,tortoise從相對于環的入口entry point移動了C-r

所以,在tortoisehare第一次在環上相遇時,環的入口entry point到這個點meet point的距離是C-r, 而從這個相遇點meet point再往前移動r,就又回到了環的入口entry point

haretortoise第一次相遇的這個時候,將haremeet point重新放到起始點start pointtortoise仍放在這個相遇點meet point
然后讓它們以相同的速度開始移動,

根據等式 B r + k*C = T,k >= 0

tortoisehare必然會在環的入口點entry point再次相遇。

入口entry point找到后,就能很容易得到T
然后入口entry point,讓tortoise停下,hare 繼續跑一圈,就能得到 C

算法應用

  1. 鏈表有環檢測
    兩個指針,慢指針移動速度為 1,快指針移動速度為 2,判斷兩個指針是否相遇
  2. 找出有環鏈表的入口節點
    當兩個指針相遇時,將其中一個指針重新放到鏈表頭,然后讓兩個指針移動速度都為 1,當兩個指針再次相遇,就找到了有環鏈表的入口節點
  3. 計算環長度
    在入口節點放置兩個個指針,一個指針不動,一個指針移動速度為 1,兩個指針相遇,就可計算出環的長度

算法實現

golang

  1. 鏈表有環檢測 leetcode 141
/*
 * @lc app=leetcode id=141 lang=golang
 *
 * [141] Linked List Cycle
 */

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func hasCycle(head *ListNode) bool {
    if head == nil {
        return false
    }
    t, h := head, head
    started := false
    for h != nil && h.Next != nil {
        if t == h {
            if started {
                return true
            } else {
                started = true
            }
        }
        t = t.Next
        h = h.Next.Next
    }
    return false
}
  1. 找出有環鏈表的入口節點 leetcode 142
/*
 * @lc app=leetcode id=142 lang=golang
 *
 * [142] Linked List Cycle II
 */

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func detectCycle(head *ListNode) *ListNode {
    var cycPoint *ListNode
    if head == nil {
        return cycPoint
    }
    t, h := head, head
    started := false
    hasCycle := false
    for h != nil && h.Next != nil {
        if t == h {
            if started {
                hasCycle = true
                break
            } else {
                started = true
            }
        }
        t = t.Next
        h = h.Next.Next
    }
    if hasCycle {
        h = head
        for h != nil && t != nil {
            if h == t {
                cycPoint = h
                break
            }
            h = h.Next
            t = t.Next
        }
    }
    return cycPoint
}

歡迎轉載,請注明出處~
作者個人主頁

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容