1. 程式人生 > 其它 >劍指offer 03:陣列中的重複數字

劍指offer 03:陣列中的重複數字

問題

找出陣列中重複的數字。

在一個長度為 n 的陣列 nums 裡的所有數字都在 0~n-1 的範圍內。陣列中某些數字是重複的,但不知道有幾個數字重複了,也不知道每個數字重複了幾次。請找出陣列中任意一個重複的數字。

示例1

輸入:[2, 3, 1, 0, 2, 5, 3]

輸出:2或者3

解法一

思路:排序+遍歷

  • 首先對資料排序,
  • 排序完成後遍歷資料,如果遇到相同的資料,立即返回。
  • 時間複雜度O(nlogn),空間複雜度O(logn)

具體實現:java

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

解法二

思路: hash表

  • 遍歷陣列,將資料作為hash表的key,個數作為value
  • 如果發現有一個key已經存在直接返回資料
  • 時間複雜度O(n),空間複雜度O(n)

具體實現:java

class Solution {
    public int findRepeatNumber(int[] nums) {
        HashMap<Integer,Integer> map = new HashMap<>(nums.length);
        for(int i = 0;i<nums.length;i++){
            if(map.get(nums[i])==null){
                map.put(nums[i],1);
            }
            else{
                return nums[i];
            }
        }
        return -1;
    }
}

解法三

思路:衝突法

分析:

可以知道所有數字都在0~n-1的範圍內,那麼我們可以知道:如果完全不衝突,必然0-n-1每個數一個,那麼我們可以將陣列下表作為key,必然有key是重複的。找到這個重複的key就好了

  • 遍歷陣列,將資料放在對應的下標。如果下標衝突,則返回資料,如果不衝突,則與當前資料交換位置
  • 比如:[2, 3, 1, 0, 2, 5, 3]
  • 第一次:[1,3,2,0,2,5,3]
  • 第二次:[3,1,2,0,2,5,3]
  • 第三次:[0,1,2,3,2,5,3]
  • 第四次,2衝突了,直接返回2
  • 時間複雜度O(n),空間複雜度O(1)
class Solution {
    public int findRepeatNumber(int[] nums) {
        for (int i = 0; i < nums.length; ) {
            if (i==nums[i]){
                i++;
                continue;
            }
            if (nums[nums[i]] == nums[i]) {
                return nums[i];
            } else {
                int t = nums[i];
                nums[i] = nums[t];
                nums[t] = t;
            }
        }

        return -1;
    }
}

問題變更一

如果需要找出全部的相同的數字,上面的方法還有效嗎?

  • 全部可以實現(建議自行嘗試)

問題變更二

在一個長度為n+1的數組裡的所有數字都在1~n範圍內,所以陣列種至少有一個數字是重複的。請找出陣列種任意一個重複的數字,但不能修改輸入的陣列

解法一

思路:複製陣列

  • 首先將陣列複製下來。
  • 然後利用原問題種的任意一種解法

程式碼參考以上

解法二

思路:二分查詢的思想

分析:

我們把1n的數字從中間的數字m分為兩部分,前面一般為1m,後面一般為m+1n。如果1m的數字的數目超過了m,那麼這一版的區間裡面一定包含重複的數字;如果兩個區間都相等,那麼將m的位置前移一位,再進行比較。以此為思想

  • 比如陣列 [1,2,2,4,5]
  • 第一次比較:m為3; 1-3和4-5分別有3個和2個。無法辨別,移動m=2
  • 第二次比較:m為2;1-2和3-5分別有3個和2個。區間1-2中一定有重複的數字
  • 第三次比較:m為1;1為1次,2為2次。重複數字為2
  • 時間複雜度O(nlogn),空間複雜度O(1)
public int findRepeatNumber(int[] nums){
    if (null == nums || nums.length == 0 || nums.length == 1) {
      return -1;
    }
    int left = 1;
    int right =  nums.length;
    int mid = left + (right - left) / 2;
    while (left <= right) {
      int preNums = mid - left + 1;
      int pre = countRange(nums ,left, mid);
      if (pre == preNums) {
        mid  = mid - 1;
        //邊界值
        if (mid < left){
          mid = left + (right - left) / 2;
          left = mid + 1;
          mid = left + (right - left) / 2;
        }
      } else if (pre < preNums) {
        if (left == right) {
          return left;
        }
        left = mid + 1;
        mid = left + (right - left) / 2;
      } else {
        if (left == right) {
          return left;
        }
        right = mid;
        mid = left + (right - left) / 2;
      }
    }
    return -1;
  }

參考:二分查詢