LeetCode——287尋找重複數 Floyd判圈

給定一個包含 n + 1 個整數的數組 nums,其數字都在 1 到 n 之間(包括 1 和 n),可知至少存在一個重複的整數。假設只有一個重複的整數,找出這個重複的數。

示例 1:

輸入: [1,3,4,2,2]
輸出: 2

示例 2:

輸入: [3,1,3,4,2]
輸出: 3

說明:

不能更改原數組(假設數組是隻讀的)。
只能使用額外的 O(1) 的空間。
時間複雜度小於 O(n2) 。
數組中只有一個重複的數字,但它可能不止重複出現一次。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/find-the-duplicate-number

題解

首先對nums數組進行建圖,每個邊都是inums[i]i\rightarrow nums[i]。由於存在重複的數字,因此這個位置的數字一定存在最少兩條邊指向它。因此整個圖一定存在一個環。而我們要找的這個重複的數字就是環的入口。

那麼如果找出環的入口呢。首先我們設置兩個指針,一個慢指針slow表示每一次只走一步,另一個指針是fast表示每一次都走兩步。根據Floyd判圈算法。兩個指針在有環的情況下一定會相遇。

在這裏插入圖片描述
我們假設環的長度爲LL,從起點到入口的步數爲aa,從環的入口繼續走bb步到達快慢指針相遇的位置。然後從相遇的位置繼續走cc步回到環的入口,那麼b+c=Lb+c=L,顯然a,b,ca,b,c都是正整數。根據上述的定義,慢指針走了a+ba+b步,而快指針走了2(a+b)2(a+b)步。從另一個角度考慮,在相遇的位置,快指針比慢指針走了若干圈環。因此快指針也可以表示爲a+b+kLa+b+kL,其kk表示快指針在環上走的圈數。因此
2(a+b)=a+b+kL2(a+b) = a+b+kL
所以a=kLba = kL-b
整理可得:
a=(k1)L+(Lb)=(k1)L+ca = (k-1)L+(L-b) = (k-1)L+c
如果快慢指針的步數都是一樣的,慢指針從起點出發,走了aa步到達了環的入口,而快指針在相遇的位置出發,走了(k1)L+c(k-1)L+c步,因爲相遇的位置,相當於從入口走了bb步,所以快指針也到達了環的入口。此時兩個指針相遇。而這個相遇點就是環的入口。就是重複的數字。
因此這個題解答分爲兩個部分,一個是快慢指針步數不一致的時候的相遇點,作爲快指針的起點。然後慢指針回到原點,快慢步數一致,然後在檢測相遇點,就是答案。

func findDuplicate(nums []int) int {
    slow, fast := 0, 0
    for slow, fast = nums[slow], nums[nums[fast]]; slow != fast; slow, fast = nums[slow], nums[nums[fast]] { }
    slow = 0
    for slow != fast {
        slow = nums[slow]
        fast = nums[fast]
    }
    return slow
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章