1. 程式人生 > 其它 >劍指offer第二版面試題3:陣列中的重複數字(java)

劍指offer第二版面試題3:陣列中的重複數字(java)

面試題3:陣列中的重複數字

題目1:在一個長度為n的數組裡的所有數字都在0到n-1的範圍內。陣列中某些數字是重複的,但不知道有幾個數字重複了,也不知道每個數字重複的次數。請找出陣列中任意一個重複的數字。例如如果輸入長度為7的陣列{2,3,1,0,2,5,3},那麼對應的輸出是重複的數字2或者3。

  • 方法一:因為所有數字都在0-n-1,將陣列排序後遍歷一遍就能知道是哪個數字重複了。陣列排序時間複雜度為O(nlogn)。

  • 方法二:將該陣列遍歷一遍存入雜湊表中,在遍歷元素時判斷,如果雜湊表中已經存在該元素,則該元素重複,如果不存在該元素,將該元素新增到集合中去。因為雜湊表的判斷是O(1)的,因此時間複雜度為O(n)(遍歷陣列),但是提升時間的代價是一個O(n)的雜湊表。

  • 方法三:因為範圍在0到n-1的範圍內,因此如果將每個位置上放置對應的腳標上的話,如果該腳標上的元素和要放入的元素相同,則有重複數字。因為每個元素至多交換兩次就能到達應有的位置上,因此時間複雜度為O(n),空間複雜度為O(1)(因為不需要額外的空間)。

    方法三的實現:(與書上大致一樣,但是缺少對輸入內容的判斷,答案採取的是for迴圈,裡面交換用的是while迴圈)

    package com.ldl.forbuilding.controller;
    
    public class HugerSingletonTest {
        public static void main(String[] args) {
            int[] arr = {2,3,1,0,2,5,3};
            System.out.println(findnumber(arr));;
        }
        public static int findnumber(int[] arr){
            int i = 0;
            while (i<arr.length){
                if (arr[i]!=i){
                    if(arr[arr[i]]==arr[i]){
                        return arr[i];
                    }
                    int temp = arr[arr[i]];
                    arr[arr[i]] = arr[i];
                    arr[i] = temp;
                }else {
                    i++;
                }
            }
            return 0;
        }
    }
    

    答案:

    public class Solution {
        public static boolean duplicate(int[] arr) {
            // 入參檢查
            if (arr == null || arr.length == 0) {
                return false;
            }
            for (int i = 0; i < arr.length; i++) {
                if (arr[i] < 0 || arr[i] >= arr.length) {
                    return false;
                }
            }
    
            // 遍歷陣列
            for (int i = 0; i < arr.length; i++) {
                while (arr[i] != i) {
                    if (arr[i] == arr[arr[i]]) {
                        System.out.println(arr[i]);
                        return true;
                    }
                    // 替換
                    int temp = arr[i];
                    arr[i] = arr[temp];
                    arr[temp] = temp;
                }
            }
            return false;
        }
    
        public static void main(String[] args) {
            int[] arr = {1, 1, 2, 4, 2, 5, 6};
            boolean result = duplicate(arr);
            System.out.println(result);
        }
    }
    

題目2:在一個長度為n+1的數組裡的所有數字都在1~n的範圍內,所以陣列中至少有一個數字是重複的。請找出陣列中任意一個重複的數字,但是不能修改輸入的陣列。例如,如果輸入長度為8的陣列{2,3,5,4,3,2,6,7},那麼對應的輸出是重複的數字2或者3。

  • 方法一:新建一個n+1大小的陣列,遍歷輸入陣列,如果該元素大小對應在新建陣列腳標位置上為空元素,則將該元素放入新陣列中,以此類推,當遍歷到的元素對應的新建陣列腳標上的元素不為空,說明出現重複元素。時間O(n),空間O(n)。

    • 手寫

      package com.ldl.forbuilding.controller;
      
      public class HugerSingletonTest {
          public static void main(String[] args) {
              int[] arr ={2,3,5,4,3,2,6,7};
              System.out.println(findnumber(arr));;
          }
          public static boolean findnumber(int[] arr){
              int[] temp = new int[arr.length];
              for (int i = 0; i < arr.length; i++) {
                  if(arr[i]==temp[arr[i]]){
                      System.out.println(arr[i]);
                      return true;
                  }else {
                      temp[arr[i]]=arr[i];
                  }
              }
              return false;
          }
      }
      
    • 答案

      public class Solution {
          public static int getDuplication(int[] arr) {
              // 入參檢查
              if (arr == null || arr.length == 0) {
                  return -1;
              }
              for (int i = 0; i < arr.length; i++) {
                  if (arr[i] < 1 || arr[i] >= arr.length) {
                      return -1;
                  }
              }
      
              int[] tempArr = new int[arr.length];
              for (int i = 0; i < arr.length; i++) {
                  if (arr[i] == tempArr[arr[i]]) {
                      return arr[i];
                  }
                  tempArr[arr[i]] = arr[i];
              }
              return -1;
          }
          public static void main(String[] args) {
              int[] numbers = {2, 1, 5, 4, 3, 2, 6, 7};
              System.out.println(getDuplication(numbers));
          }
      }
      

    假如面試官要求空間複雜度為O(1)時,我們要用到方法二。

  • 方法二:通過二分查詢(折半查詢)的方式,判斷1m中的元素出現的次數,如果小於m次,則重複元素在m+1n中。將m+1~n再進行對半拆分,判斷元素出現次數。 舉例說明:長度為8的陣列,元素範圍為17。那麼14出現次數為5次(通過直接遍歷陣列,判斷每個元素是不是大於等於1且小於等於4),大於4,因此14中一定有重複元素。再拆分,12出現次數為2次(注意,這個時候2也是重複元素,但是該方法判斷不出來!),34出現了3次。因此34中一定有重複元素。再拆分,3出現了2次,4出現了一次,找到重複元素3。

    • 手寫(看答案寫出來的。。。二分查詢忘乾淨了)

      package com.ldl.forbuilding.controller;
      
      public class HugerSingletonTest {
          public static void main(String[] args) {
              int[] arr ={2,3,5,4,3,2,6,7};
              System.out.println(findnumber(arr));;
          }
          public static int findnumber(int[] arr){
              // 入參檢查
              if (arr == null || arr.length == 0) {
                  return -1;
              }
              for (int i = 0; i < arr.length; i++) {
                  if (arr[i] < 1 || arr[i] >= arr.length) {
                      return -1;
                  }
              }
              int left = 1;
              int right = arr.length-1;
              while (left<=right){
                  int mid = (right-left)/2+left;
                  int count = findcount(arr,left,mid);
                  if(left==right){
                      if(count>1){
                          return left;
                      }else {
                          break;
                      }
                  }
                  if(count>mid-left+1){
                      right=mid;
                  }else {
                      left=mid+1;
                  }
              }
              return -1;
          }
          public static int findcount(int[] arr,int left,int right){
              int count =0 ;
              for (int i = 0; i <arr.length ; i++) {
                  if(arr[i]>=left&&arr[i]<=right){
                      ++count;
                  }
              }
              return count;
          }
      }
      

      二分查詢O(logn),遍歷長度為n的陣列O(n),因此輸入長度為n的陣列,那麼findcount函式將被呼叫O(logn)次,每次時間為O(n),因此總時間複雜度為O(nlogn),空間複雜度為O(1)。與之前的時間和空間都為O(n)相比是時間換空間。