❝
成功呈概率分佈,關鍵是你能不能堅持到成功開始呈現的那一刻。—— 佚名
❞
不修改數組找出重複的數字
題目描述
在一個長度爲 n+1 的數組裏的所有數字都在 1 到 n 的範圍內,所以數組中至少有一個數字是重複的。請找出數組中任意一個重複的數字,但不能修改輸入的數組。例如,如果輸入長度爲 8 的數組 {2, 3, 5, 4, 3, 2, 6, 7},那麼對應的輸出是重複的數字 2 或者 3。
解法
解法一
創建長度爲 n+1 的輔助數組,把原數組的元素複製到輔助數組中。如果原數組被複制的數是 m,則放到輔助數組第 m 個位置。這樣很容易找出重複元素。空間複雜度爲 O(n)。
解法二
數組元素的取值範圍是 [1, n],對該範圍對半劃分,分成 [1, middle], [middle+1, n]。計算數組中有多少個(count)元素落在 [1, middle] 區間內,如果 count 大於 middle-1+1,那麼說明這個範圍內有重複元素,否則在另一個範圍內。繼續對這個範圍對半劃分,繼續統計區間內元素數量。
時間複雜度 O(n * log n),空間複雜度 O(1)。
注意,此方法無法找出所有重複的元素。
public class Solution {
/**
* 不修改數組查找重複的元素,沒有則返回-1
* @param numbers 數組
* @return 重複的元素
*/
public int getDuplication(int[] numbers) {
if (numbers == null || numbers.length < 1) {
return -1;
}
int start = 1;
int end = numbers.length - 1;
while (end >= start) {
int middle = start + ((end - start) >> 1);
// 調用 log n 次
int count = countRange(numbers, start, middle);
if (start == end) {
if (count > 1) {
return start;
}
break;
} else {
// 無法找出所有重複的數
if (count > (middle - start) + 1) {
end = middle;
} else {
start = middle + 1;
}
}
}
return -1;
}
/**
* 計算整個數組中有多少個數的取值在[start, end] 之間
* 時間複雜度 O(n)
* @param numbers 數組
* @param start 左邊界
* @param end 右邊界
* @return 數量
*/
private int countRange(int[] numbers, int start, int end) {
if (numbers == null) {
return 0;
}
int count = 0;
for(int e : numbers) {
if (e >= start && e <= end) {
++count;
}
}
return count;
}
}
測試用例
- 長度爲 n 的數組中包含一個或多個重複的數字;
- 數組中不包含重複的數字;
- 無效測試輸入用例(輸入空指針)。
我把我寫的所有題解整理成了一本電子書放在了 github 上,三天內衝擊到 github 排行榜榜首!近 5w 人下載閱讀!要獲取的話,直接進入下方鏈接就可以了(記得給我點個 star):