LeetCode-287: Find the Duplicate Number(尋找重複數字——弗洛伊德循環尋找算法)

Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.

Example 1:

Input: [1,3,4,2,2]
Output: 2

Example 2:

Input: [3,1,3,4,2]
Output: 3

Note:

  1. You must not modify the array (assume the array is read only).
  2. You must use only constant, O(1) extra space.
  3. Your runtime complexity should be less than O(n2).
  4. There is only one duplicate number in the array, but it could be repeated more than once.

思路

包含有n+1個不大於n的正整數的數組一定存在重複元素,本題要求從這樣的數組中找出重複的那個元素。題目很簡單,有很多方法可做,但是難在要求空間複雜度O(1),時間複雜度小於O(n2),並且源數組是隻讀的,不允許任何修改操作。這樣就限制了諸如Hash表,排序等的解法。最優解法是採用弗洛伊德循環尋找算法,原理如下:

  1. 需要明確一點:n+1個不大於n的正整數組成的數組可看做一個鏈表:
    • A[0]=1 >>>> A[1]=3 >>>> A[3]=2 >>>> A[2]=4 >>>> A[4]=2
i 0 1 2 3 4
A[i] 1 3 4 2 2

如果這樣的數組中出現重複元素,那麼這個鏈表就會出現一個環

  • A[0]=1 >>>> A[1]=3 >>>> A[3]=2 >>>> A[2]=4 >>>> A[4]=2
  1. 弗洛伊德循環尋找算法就是查找這個環入口的算法,過程爲:
    (1)算法採用了一個慢指針和一個快指針,慢指針每次移動1步,快指針每次移動2步,初始兩個指針都指向0,由於路徑中存在環,兩個指針最終肯定會匯合。第一次匯合時,我們將快指針重置爲0,並將移動步數從2修改爲1,等到兩個指針再次匯合時,指向的元素即爲環入口。
    (2)原理推導:如下圖,P段表示鏈表中的非環部分,C表示慢指針與快指針第一次匯合時在環中重合的部分,L表是整個環的長度,X表示L-C部分;
    鏈表環
    (3)則當快指針與慢指針第一次匯合時,慢指針走過的路徑長度爲P+C,快指針走過的路徑長度爲L+C+nL(n爲整數,由於快指針速度快,因此快指針一定是比慢指針多跑了n圈);然後由於快指針的速度是慢指針速度的2倍,而他們經過的時間是相同的,因此路程有如下關係:
    P+C+nL=2(P+C)=>P=(n1)L+LC=(n1)L+X P + C + nL = 2 (P + C) => P = (n - 1) * L + L - C = (n - 1) L + X
    因此可得出結論:“P段的長度”等於“X段的長度+整數圈環的長度”,故此時只需要讓快指針重置爲0(即回到P段的開始)並使速度與慢指針相同,則他們一定會在環路入口的位置再次匯合。

Java實現

class Solution {
    public int findDuplicate(int[] nums) {
        int slow = 0;
        int fast = 0;
        do {
            slow = nums[slow];
            fast = nums[nums[fast]];
        }while (slow != fast);

        fast = 0;

        while (slow != fast) {
            slow = nums[slow];
            fast = nums[fast];
        }
        return slow;
    }
}

Python實現

class Solution(object):
    def findDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        slow = fast = 0
        while True:
            slow = nums[slow]
            fast = nums[nums[fast]]
            if slow == fast:
                break
        
        fast = 0

        while slow != fast:
            slow = nums[slow]
            fast = nums[fast]

        return fast

Scala實現

object Solution {
    def findDuplicate(nums: Array[Int]): Int = {
        var slow = 0
        var fast = 0
        do {
            slow = nums(slow)
            fast = nums(nums(fast))
        } while (slow != fast)

        fast = 0

        while (slow != fast) {
            slow = nums(slow)
            fast = nums(fast)
        }

        return fast
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章