287. 尋找重復數

這題的難點就在於下面的說明了,我們先不管下面的那些說明的要求,用常規的解法來解答下上的題目。
排序思想解法
先把原來的數組進行排序,然后逐個遍歷,一旦發現后一個元素和當前的元素相等,那么就返回,這就是我們找到了重復數字。但是這種思想,就不滿足說明里面的,不能改變原數組,雖然時間復雜度是滿足O(n^2)。
哈希思想
用個哈希集合(HashSet)來記錄已經出現過的元素,一旦遍歷到了元素曾經出現在集合當中,那么就返回,這就是需要尋找的重復數字。
重新排序的思想
這種思想說起來有點復雜,但是時間復雜度是最好的。
我們從頭開始遍歷數組,遍歷到的下標為i,那么我們就分兩種情況來討論:
- 如果num[i]等於(i+1),就是值等於剛好等於下標那么就遍歷到下一個。因為剛剛好這個就是對應的,我們就不管他了。
- 如果num[i]和(i+1)不等,那么就去吧下標等於(num[i] - 1)的數字和這個數字進行交換(下標為i),這樣再去判斷如今的這個位置上的value和index是否相等,如果不等,繼續交換。交換到這個位置上的num[i]和(i+1)相等為止;或者你會找到一個數字,這個數字,那個數字在對應的位置上已經有了,那么這個就是重復的那個數字了。
Talk is cheap, show me the code.
public int findDuplicate(int[] nums) {
if (nums.length == 0) {
return 0;
}
int res = 0;
for (int i = 0, len = nums.length; i < len; i++) {
int temp = i + 1;
// 判斷num[i]的值是不是就是放在這
if (temp == nums[i]) {
continue;
} else {
// 兩個不相同就進入循環,
while (temp != nums [i]) {
int newIndex = nums[i] - 1;
// 如果位置上的數字和num[i]相等,那么就表示出現重復的數字,
if (nums[newIndex] == nums[i]) {
return nums[i];
}
// 交換兩個元素
int swapTemp = nums[newIndex];
nums[newIndex] = nums[i];
nums[i] = swapTemp;
}
}
}
return res;
}
當然了,以上依然不是最好的解法。因為雖然時間是O(n),但是卻把原來的數組變動了。
用二分思想
這里的思想有點復雜,大概的思想是這樣:
我們先假設一個如果排序好的數組中,你如果取中間的數字,那么如果你的這個中間數 是要比當前的索引的坐標大的話,那么就是也就是nums[i] > i,那么就是說明那個重復的數字是在后半部分的,因為只有在后半部分有重復數字存在的時候,才會多出一個數字來,那么我們就用二分法,吧start取到中點位置,繼續尋找;反之,那個重復的數字是在前半部的。
因為我們這數組是沒排序的數組,那么我們根據上面的那個計數的思想,我們先取一個取值范圍,如果數組里面的元素的取值在這個取值范圍的元素個數,等於這個取值范圍的區間,那么就表示這個取值范圍內不存在重復元素,我們要取別的區間的,繼續計數。
public int findDuplicateNew(int[] nums) {
int start = 1;
int end = nums.length;
while (start <= end) {
// 取中值
int middle = start + ((end - start) >> 1);
// 計算從開始值到中值區間內有多少數字。
int tempCount = countRange(nums, start, middle);
// 如果已經區間已經縮小的到了只有一個數了,那么就可以判斷在區間內的數字是不是有兩個了。
if (start == end) {
if (tempCount > 1) {
return start;
} else {
break;
}
}
// 區間就是 中值- 開始值 + 1。然后開始和計數比較。
int range = middle - start + 1;
if (tempCount > range) {
end = middle;
} else if (tempCount <= range) {
start = middle + 1;
}
}
return -1;
}
// 計數比較,時間復雜度為O(n)
private int countRange(int[] nums, int start, int end) {
int count = 0;
for (int item : nums) {
if (item >= start && item <= end ) {
count++;
}
}
return count;
}
以上的時間復雜度是$O(NlogN)$ ,二分的時間復雜度是$O(logN)$,每次計數的時間復雜度是$O(N)$。
