題目
給定一個長度為 n 的整數數組 nums,數組中所有的數字都在 0∼n−1 的范圍內。
數組中某些數字是重復的,但不知道有幾個數字重復了,也不知道每個數字重復了幾次。
請找出數組中任意一個重復的數字。
注意:如果某些數字不在 0∼n−1 的范圍內,或數組中不包含重復數字,則返回 -1;
樣例
給定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。
返回 2 或 3。
算法思路
找重復數字可以直接用HashMap來解決,但需要額外的空間。
本題可以只用O(1)的空間復雜度來實現,具體思路是:
由於數字范圍是[0, n-1],數組長度是n,因此可把數組當做是數字的槽位,依次將數字放到對應數組下標的位置去。
遍歷數組,將當前元素nums[i]放到它對應的數組下標的位置去,即swap(nums[i] , nums[nums[i]]),注意交換到位置i的數字也要繼續放到它相應的槽去,依次循環直到當前位置i放入了正確的數字,即nums[i] == i。
假如在將當前位置的元素放到正確位置的時候,發現該位置已經是正確的數字了,說明有重復,直接返回該數字即可。
需要注意,題目中“如果某些數字不在 0∼n−1 的范圍內,則返回 -1”,這是個坑,需要提前判斷,如果在遍歷數組中判斷則可能先找到重復元素,就退出,超出范圍的數還沒被判斷到。
算法的時間復雜度是O(n)
代碼實現
class Solution {
public int duplicateInArray(int[] nums) {
if (nums == null || nums.length == 0) return -1;
int n = nums.length;
//提前遍歷判斷"如果某些數字不在 0∼n−1 的范圍內,則返回 -1"
for (int i=0; i<n; i++) {
if (!(nums[i] >= 0 && nums[i] < n)) return -1;
}
for (int i=0; i<n; i++) {
//循環將當前位置的元素放到對應的槽去
while (nums[i] != nums[nums[i]]) swap(nums, i, nums[i]);
//當前元素應該放到對應位置,但該位置的數字跟當前的一樣,即重復了
if (nums[i] != i && nums[i] == nums[nums[i]]) return nums[i];
}
return -1;
}
//交換數組兩個元素
private void swap(int[] nums, int i, int j) {
nums[i] = nums[i] ^ nums[j];
nums[j] = nums[i] ^ nums[j];
nums[i] = nums[i] ^ nums[j];
}
}
