题目描述
给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。找到所有在 [1, n] 范围之间没有出现在数组中的数字。(要求 O ( n ) + O ( 1 ) O(n) + O(1) O(n)+O(1))
输入:[4,3,2,7,8,2,3,1]
输出:[5,6]
解题思路
和《剑指offer》上的类似题目不太一样,此题比较有技巧性,不太好想。
-
哈希表:这种计数题肯定可以用哈希表解决,但是这里不满足空间复杂度要求。
-
桶排序(推荐):由于数组的元素取值范围是
[1, N]
,我们可以利用“一个萝卜一个坑”的原理,在输入数组本身以某种方式标记已访问过的数字,然后再找到缺失的数字。- 遍历输入数组的每个元素一次。
- 我们将把
|nums[i]|-1
索引位置的元素标记为负数。即nums[|nums[i]|-1] * -1
。(标记) - 然后遍历数组,若当前数组元素
nums[i]
为负数,说明我们在数组中存在数字i+1
。
实际上相当于利用正负号构建了一个简易的哈希表,用来存储每个数字的状态,非常巧妙!
进阶:若将题目要求改为数组中每个元素出现的可能次数是 n 次,求出数组中出现此次为偶数(奇数)次的元素(出现 0 次也算偶数次)。
解答:将所有正数作为数组下标,置对应数组值为相反数。那么,仍为正数的位置即为出现偶数次(未出现是0次,也是偶数次)数字。
-
奇技淫巧:看看就好,技巧性太强。
参考代码
class Solution {
public:
vector<int> findDisappearedNumbers(vector<int>& nums) {
int length = nums.size();
if(length == 0)
return vector<int>();
vector<int> res;
for(int i = 0; i < length; i++){
int num = abs(nums[i]);
nums[num - 1] = -abs(nums[num - 1]); // 标记(要注意防止重复标记,这里用if改写也可以)
}
for(int i = 0; i < length; i++){
if(nums[i] > 0)
res.push_back(i+1);
}
return res;
}
};