不修改數組找出重複的數字。
在一個長度爲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,說明這個數字就是重複的數字,任務結束。
見實例代碼中的findRepeatNumberBySegment。code
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 }