查找數組中重複的數字(二)

需求

不修改數組找出重複的數字。
在一個長度爲n+1的數組裏的全部數字都在1~n的範圍內,因此數組中至少有一個數字是重複的。請找出數組中任意一個重複的數字,單不能修改輸入的數組。例如,若是輸入長度爲8的數組{2,3,5,4,6,2,6,7},那麼對應的輸出是重複的數字2或者6。(不要求找出全部重複數字,只要找出其中之一便可)ios

 

分析

因爲不能修改輸入的數組,所以不能在原始數組中作排序再判斷下標。須要考慮其餘的方法。c++

思路一:

使用一個輔助的數組,長度爲n,如:tmp[n],而後開始遍歷原始數組
1.若是數組中出現數字x,就判斷數組下標爲x的節點是否仍是初始值0,即tmp[x]是否爲0;
2.若是tmp[x] == 0,就標記爲1,而後繼續遍原始數組的下一個元素,繼續步驟1;
3.若是tmp[x] != 0,說明以前已經表標記爲1了,即以前已經出現過這個相同的元素,即已經找到重複數字了。
見示例代碼中的findRepeatNumberWithExArray數組

 

思路二:

暫且稱之爲分段法。spa

假如數組以下{2,3,5,4,6,2,6,7}
數組的取值範圍在1~7,數組的長度爲8,因此在1~7裏面確定是有1個或者1個以上重複的數字。
1.把1~7的取值範圍分紅兩部分,前半部分爲1~4,後半部分是5~7;
2.計算1~4的數字有多少個,今後例子來看,一共有4個(2,3,4,2),1~4之間範圍爲4,數組的長度爲4,有可能有重複的數字也有可能沒有重複的數字;
3.因爲剩下的數字範圍是5~7範圍爲3,數組長度是4(總長度8減去前半部分數字的長度,就是後半部分剩餘的長度4),所以確定有重複數字;
4.查找範圍變成5~7,重複步驟1,把取值範圍分紅兩部分,前半部分是5~6,後半部分是7;接着繼續步驟二;
...
最後.當查找的數字範圍變成1,而且計算得出的數字個數大於1,說明這個數字就是重複的數字,任務結束。
見實例代碼中的findRepeatNumberBySegmentcode

 

c++示例代碼以下:

  1 #include <iostream>
  2 
  3 using namespace std;
  4 
  5 const int ARR_LENGTH = 8;
  6 
  7 /************************************************************************/
  8 /* @brief 使用額外的輔助數組查找數組中其中一個重複數字
  9 /* @param arr數組
 10 /* @param length數組長度
 11 /* @return 數組中其中一個重複的數字,-1表示沒找到
 12 /************************************************************************/
 13 int findRepeatNumberWithExArray(const int* arr, const int length)
 14 {
 15     int result = -1;
 16 
 17     //入參有問題直接返回,少於2個數字確定沒有重複的
 18     if (!arr || length < 2)
 19     {
 20         return result;
 21     }
 22 
 23     //一個臨時的輔助數組
 24     int *tmpArr = new int[length];
 25     memset(tmpArr, 0, sizeof(int)*length);
 26 
 27     for (int i = 0; i < length; ++i)
 28     {
 29         if (tmpArr[arr[i]] != 0)
 30         {
 31             result = arr[i];
 32             break;
 33         }
 34         else
 35         {
 36             tmpArr[arr[i]] = 1;
 37         }
 38     }
 39     delete[] tmpArr;
 40     tmpArr = nullptr;
 41     return result;
 42 }
 43 
 44 /************************************************************************/
 45 /* @brief 計算數組中在某一個範圍內的數字的數量
 46 /* @param arr數組
 47 /* @param length數組長度
 48 /* @param start範圍開始位置(包括start)
 49 /* @param end範圍結束位置(包括end)
 50 /* @return 在範圍內的數字的數量
 51 /************************************************************************/
 52 int countRange(const int* arr, const int length, const int start, const int end)
 53 {
 54     int counter = 0;
 55     if (!arr || length < 1 || end < start)
 56     {
 57         return counter;
 58     }
 59 
 60     for (int i = 0; i < length; ++i)
 61     {
 62         //若是數字在範圍內則把個數加1
 63         if (arr[i] >= start && arr[i] <= end)
 64         {
 65             counter++;
 66         }
 67     }
 68     return counter;
 69 }
 70 
 71 /************************************************************************/
 72 /* @brief 使用分段法查找數組中其中一個重複數字
 73 /* @param arr數組
 74 /* @param length數組長度
 75 /* @return 數組中其中一個重複的數字,-1表示沒找到
 76 /************************************************************************/
 77 int findRepeatNumberBySegment(const int* arr, const int length)
 78 {
 79     int result = -1;
 80 
 81     //入參有問題直接返回,少於2個數字確定沒有重複的
 82     if (!arr || length < 2)
 83     {
 84         return result;
 85     }
 86 
 87     int start = 1;
 88     int end = length - 1;
 89     int counter = 0;
 90 
 91     while (start <= end)
 92     {
 93         int middle = (end - start) / 2 + start;
 94         //計算前半部分的數字的數量
 95         counter = countRange(arr, length, start, middle);
 96         //只有一個數字而且數量大於1,說明找到了重複的數字了
 97         if (start == middle && counter > 1)
 98         {
 99             result = start;
100             break;
101         }
102         
103         //假如前半部分數字數量少於等於範圍,說明這個範圍可能有重複的也有可能沒有重複的數字,可是後一半的數字確定有重複的,直接在後一半查找
104         if (counter <= (middle - start + 1))
105         {
106             start = middle + 1;
107         }
108         //假如前半部分數字的數量大於數字範圍,說明這個範圍內確定有重複的數字,繼續查找
109         else
110         {
111             end = middle;
112         }
113     }
114     return result;
115 }
116 
117 int main()
118 {
119     int arr[ARR_LENGTH] = {2,3,5,4,6,2,6,7};
120 
121     cout << "原始數據:" << endl;
122 
123     for (int i = 0; i < sizeof(arr) / sizeof(int); ++i)
124     {
125         cout << arr[i] << "  ";
126     }
127 
128     int repeat = findRepeatNumberWithExArray(arr, ARR_LENGTH);
129     cout << "\n\n輔助數組法查找重複數字:" << repeat << endl;
130 
131     repeat = findRepeatNumberBySegment(arr, ARR_LENGTH);
132     cout << "\n\n分段法查找重複數字:" << repeat << endl;
133 
134     return 0;
135 }

 

運行結果:

相關文章
相關標籤/搜索