數組中重複的數字-劍指offer面試題3

找出數組中重複的數字

問題描述:

力扣鏈接
在一個長度爲 n 的數組 nums 裏的所有數字都在 0~n-1 的範圍內。數組中某些數字是重複的,但不知道有幾個數字重複了,也不知道每個數字重複了幾次。請找出數組中任意一個重複的數字。

方法一:排序做法

將數組進行排序,排序後再查找重複數字。

class Solution {
    public int findRepeatNumber(int[] nums) {
            Arrays.sort(nums);
            int pre = nums[0];
            for(int index=1; index < nums.length; index++){
                if(pre == nums[index])
                    return pre;
                pre = nums[index];
            }
        return -1;
    }
}

時間複雜度:O(nlogn),空間複雜度:O(1)

方法二:哈希表

從頭到尾按順序掃描數組的每個數字,每掃描到一個數字的時候,都可以用O(1)的時間來判斷哈希表裏是否存在這個數字,如果不存在的話,就把該數字加入到這個哈希表中,如果存在的話就說明該數字重複。

class Solution {
    public int findRepeatNumber(int[] nums) {
        int n = nums.length;
        Set<Integer> set = new HashSet<Integer>();
        for(int index=0;index<n;index++){
            if(set.contains(nums[index]))
                return nums[index];
            set.add(nums[index]);
        }
        return -1;
    }
}

時間複雜度:O(n),空間複雜度:O(n)

方法三:原地置換法

即書中所提到的方法:當掃描到下標爲i的數字時,首先比較這個數字(用m表示)是否等於i,如果相等,掃面下一個數字;如果不相等,將它與下標爲m的數組中數字進行比較,如果相等就說明該數字時重複的(再下標和im的位置都出現了),如果不相等,就把下標爲i(值爲m)與下標爲m的數組中的兩個數進行互換,把m放到屬於他的位置。接下來重複這個比較、交換的過程,直到我們發現一個重複的數字。

class Solution {
    public int findRepeatNumber(int[] nums) {
        int n = nums.length;
        for(int index=0;index<n;index++){
            while(nums[index] != index){
                if(nums[index] == nums[nums[index]])
                    return nums[index];
                 int temp = nums[index];
                 nums[index] = nums[temp];
                 nums[temp] = temp;
            }
        }
        return -1;
    }
}

小心錯誤交換方式:num[index]的值再第二行時就已經改變了

				int temp = nums[index];
                nums[index] = nums[nums[index]];
                nums[nums[index]] = temp;

時間複雜度:O(n),空間複雜度:O(1)。時間複雜度解釋:代碼中經管有一個二重循環,但每個數字最多兩次交換就能找到屬於它的位置。
總結:第一種方式最簡單明瞭,但是時間複雜度最高,空間複雜度較低;第二種方法時間複雜度較低,但是空間複雜度最高;第三種方式時間複雜度和空間複雜度都是最低的,也就是書中的方法。

這題是不能用二分解的,原因就出在數組的長度上面,因爲278題長度n+1,1 ~ n代表必定有一個數字出現兩次,所以你用二分有個區間的數字在原數組中出現次數是>這個區間範圍的,但是對這題而言,長度n,0 ~ n-1且有重複,代表有數重複,就有數不存在,這樣長度才符合,這將會導致可能重複的數和不存在的數在同一個區間你無法找出來。

不修改數組找到重複數字

問題描述:

力扣鏈接
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.

原地置換法

由於不能修改數組,可以使用一個輔助數組來存儲上述的數組,然後再用原地置換法解決問題,這種方法的時間複雜度爲O(n),空間複雜度爲O(n)。參考上面的代碼即可。

二分查找法

將1 ~ n的數字從中間的數字m分爲兩個部分,前面一半是1 ~ m,後面的一半爲m+1 ~ n,如果1 ~ m中的數字的個數超過了m,那麼這一區間一定重複的數字,否則,另一個區間裏一定會有重複的數字。將有重複的數字的區間再一分爲二,直到找到哪個重複的數字。

class Solution {
    public int findDuplicate(int[] nums) {
        int n = nums.length;
        int start = 1;
        int end = n -1;
        while (end >= start) {
            int middle = ((end - start) >> 1) + start;
            int count = countRange(nums,start,middle);
            if (end == start) {
                if (count > 1)
                    return start;
                else
                    break;
            }
            if (count > (middle - start + 1)) {
                end = middle;
            } else {
                start = middle + 1;
            }
        }
        return -1;
    }

    int countRange(int[] nums, int start, int end) {
        int count = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] >= start && nums[i] <= end)
                count++;
        }
        return count;
    }
}

輸入長度爲n的數組,那麼函數countRange將被調用O(logn)次,每次需要O(n)的時間。所以該方法的時間複雜度爲:O(nlogn),空間複雜度爲O(1)。時間換空間。

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