Find the Duplicate Number (尋找重復數字)


對於一個長度為n+1的數組,其中每一個值的取值范圍是[1,n],可以證明的是必然存在一個重復數字(抽屜原理),假設僅存在一個重復數字,找到他。

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

自己做的時候,要么時間復雜度到o(n2),要么需要額外的存儲空間利用hashset,下面來分析一下leetcode上別人的算法吧。

方法一:通過圖論中環的有關知識解決

public int findDuplicate(int[] nums) {
        // Find the intersection point of the two runners.
        int tortoise = nums[0];
        int hare = nums[0];
        do {
            tortoise = nums[tortoise];
            hare = nums[nums[hare]];
        } while (tortoise != hare);

        // Find the "entrance" to the cycle.
        int ptr1 = nums[0];
        int ptr2 = tortoise;
        while (ptr1 != ptr2) {
            ptr1 = nums[ptr1];
            ptr2 = nums[ptr2];
        }

        return ptr1;
    }

如果數組的每一個數的取值都是不重復的,那么可以選取特定的數值來使,不斷通過索引值得到數值,再將新的數值作為索引值,循環下去可以得到一個鏈路。假定數組為[1,2,3,4,5],設定f(x),x是索引值,f(x)是值,並將得到的f(x)作為下一個輸入,這樣形成了一個鏈路,1->2->3->4->5;但是如果稍微做一下改變原數組為[1,2,3,4,1],那么鏈路將變為,1->2->3->4->1…,形成一個環路,這里的重復數字1就是構成環路的關鍵。

本題中就包含這樣一個重復數字,所以數組nums一定會存在一個環路,問題變為如何查找環路起點問題,對於這種問題有這樣一個算法,叫做弗洛伊德的循環尋找算法。在算法中會有兩個指針一個快速指針每次移動兩個步驟,一個慢速指針每次移動一個步驟,其中快速指針會提前進入循環並且在慢速指針進入循環后會與其相交。類似於操場跑圈,快速指針和慢速指針同時同宿舍出發,快速指針先到操場開始跑圈,慢速指針后到操場開始跑圈,但是快速指針一定會在某個時刻與慢速指針到達同一位置。

如何求取圓環起點位置(即重復的數字):

設定慢速指針與快速指針相交點距離環起點的距離為k,環周長為n,指針起點到環起點的距離為m,則慢速指針走過的距離為a = m+k+xn ;快速指針走過的距離為2a = m+k+yn,兩者做差可以得到a = (y-x)n,是環長度的整數倍,如果將快速指針重置到起點,且將快速指針的移動距離改為1,那么當快速指針移動到圓環起點時,慢速指針移動距離為a+m,因為a是圓環長度的整數倍,所以慢速指針的位置也是在圓環起點,這樣兩者的相遇點即為圓環起點。

方法二:

    public int findDuplicate_leetcode3(int[] nums) {
        if (nums.length == 0 || nums == null)
            return 0;
        int low = 1, high = nums.length - 1, mid;
        while (low < high) {  //這個題不好用low+1<high,因為最后結果的兩個值無從比較,是基於count的
            mid = low + (high - low) / 2;
            int count = 0;
            for (int i = 0; i < nums.length; i++) {
                if (nums[i] <= mid)
                    count++;
            }
            if (count > mid)
                high = mid;
            else
                low = mid + 1;
        }
        return low;
    }

根據題目中的條件,可以知道的是一定會有重復數字且只有一個重復數字,那么必然導致數組數值的不均勻分布(指的不是位置的不均勻,而是數值的取值不均勻),那么可以通過二分法來判別重復取值的數字在哪個部分,取數組的中值,計算大於和小於中值的數量,取數量較多的那份為新的數組,重復進行,最后到不可分時,得到的即為重復數字。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM