287. 尋找重復數 Java解法


287. 尋找重復數

這題的難點就在於下面的說明了,我們先不管下面的那些說明的要求,用常規的解法來解答下上的題目。

排序思想解法

先把原來的數組進行排序,然后逐個遍歷,一旦發現后一個元素和當前的元素相等,那么就返回,這就是我們找到了重復數字。但是這種思想,就不滿足說明里面的,不能改變原數組,雖然時間復雜度是滿足O(n^2)。

哈希思想

用個哈希集合(HashSet)來記錄已經出現過的元素,一旦遍歷到了元素曾經出現在集合當中,那么就返回,這就是需要尋找的重復數字。

重新排序的思想

這種思想說起來有點復雜,但是時間復雜度是最好的。
我們從頭開始遍歷數組,遍歷到的下標為i,那么我們就分兩種情況來討論:

  1. 如果num[i]等於(i+1),就是值等於剛好等於下標那么就遍歷到下一個。因為剛剛好這個就是對應的,我們就不管他了。
  2. 如果num[i]和(i+1)不等,那么就去吧下標等於(num[i] - 1)的數字和這個數字進行交換(下標為i),這樣再去判斷如今的這個位置上的value和index是否相等,如果不等,繼續交換。交換到這個位置上的num[i]和(i+1)相等為止;或者你會找到一個數字,這個數字,那個數字在對應的位置上已經有了,那么這個就是重復的那個數字了。

Talk is cheap, show me the code.

public int findDuplicate(int[] nums) {
    if (nums.length == 0) {
        return 0;
    }
    int res = 0;
    for (int i = 0, len = nums.length; i < len; i++) {
        int temp = i + 1;
//          判斷num[i]的值是不是就是放在這
        if (temp == nums[i]) {
            continue;
        } else {
//              兩個不相同就進入循環,
            while (temp != nums [i]) {
                int newIndex = nums[i] - 1;
//                  如果位置上的數字和num[i]相等,那么就表示出現重復的數字,
                if (nums[newIndex] == nums[i]) {
                    return nums[i];
                }
//                    交換兩個元素
                int swapTemp = nums[newIndex];
                nums[newIndex] = nums[i];
                nums[i] = swapTemp;
            }
        }
    }
    return res;
}

當然了,以上依然不是最好的解法。因為雖然時間是O(n),但是卻把原來的數組變動了。

用二分思想

這里的思想有點復雜,大概的思想是這樣:
我們先假設一個如果排序好的數組中,你如果取中間的數字,那么如果你的這個中間數 是要比當前的索引的坐標大的話,那么就是也就是nums[i] > i,那么就是說明那個重復的數字是在后半部分的,因為只有在后半部分有重復數字存在的時候,才會多出一個數字來,那么我們就用二分法,吧start取到中點位置,繼續尋找;反之,那個重復的數字是在前半部的。
因為我們這數組是沒排序的數組,那么我們根據上面的那個計數的思想,我們先取一個取值范圍,如果數組里面的元素的取值在這個取值范圍的元素個數,等於這個取值范圍的區間,那么就表示這個取值范圍內不存在重復元素,我們要取別的區間的,繼續計數。

public int findDuplicateNew(int[] nums) {
    int start = 1;
    int end = nums.length;
    while (start <= end) {
//            取中值
        int middle = start + ((end - start) >> 1);
//            計算從開始值到中值區間內有多少數字。
        int tempCount = countRange(nums, start, middle);
//            如果已經區間已經縮小的到了只有一個數了,那么就可以判斷在區間內的數字是不是有兩個了。
        if (start == end) {
            if (tempCount > 1) {
                return start;
            } else {
                break;
            }
        }

//            區間就是 中值- 開始值 + 1。然后開始和計數比較。
        int range = middle - start + 1;
        if (tempCount > range) {
            end = middle;
        } else if (tempCount <= range) {
            start = middle + 1;
        }
    }
    return -1;
}

//    計數比較,時間復雜度為O(n)
private int countRange(int[] nums, int start, int end) {
    int count = 0;
    for (int item : nums) {
        if (item >= start && item <= end ) {
            count++;
        }
    }
    return count;
}

以上的時間復雜度是$O(NlogN)$ ,二分的時間復雜度是$O(logN)$,每次計數的時間復雜度是$O(N)$。


免責聲明!

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



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