645. Set Mismatch
题目
集合
s
包含从1
到n
的整数。不幸的是,因为数据错误,导致集合里面某一个数字复制了成了集合里面的另外一个数字的值,导致集合 丢失了一个数字 并且 有一个数字重复 。给定一个数组
nums
代表了集合S
发生错误后的结果。请你找出重复出现的整数,再找到丢失的整数,将它们以数组的形式返回。
示例 1:
输入:nums = [1,2,2,4]
输出:[2,3]
示例 2:
输入:nums = [1,1]
输出:[1,2]
提示:
2 <= nums.length <= 104
1 <= nums[i] <= 104
题解
思路1:哈希表
以数字的值为 key,以数字是否出现过为 value,存入哈希表中。在存储的过程中若发现该 key 对应的 value 已经为 true,则该 key 为重复的数字。存储完成后遍历哈希表,如果某个 key 对应的 value 为 false,则该 key 为丢失的数字。
在[LeetCode 451 - 根据字符出现频率排序]中介绍了使用数组模拟Map的方法,此例中该方法依然适用。
class Solution {
public int[] findErrorNums(int[] nums) {
//创建map用于指示数字是否出现过
boolean[] map = new boolean[nums.length];
int[] res = new int[2];
//存储的同时查询数字是否为第二次出现
for(int num : nums){
if(map[num - 1]){
res[0] = num;
}else{
map[num - 1] = true;
}
}
//遍历hash表,查找丢失的数字
for(int i = 0; i < map.length; i++){
if(!map[i]){
res[1] = i + 1;
break;
}
}
return res;
}
}
思路2:位运算
如果将 nums
和1
~ n
这2n个数字异或,最后的结果 xor
将会是丢失的数字 n1
与重复的数字 n2
的异或。xor
中的每一个值都是 n1
与 n2
的二进制表示中不相同的位。
取其中的一位 bit
与 nums
逐个异或,可以将 nums
分成两组,一组应该存在 n1
但它缺失了,另一组 n2
出现了两次。
取 bit
和 1
~ n
逐个异或同样可以将其分成两组。将对应的组别异或即可得到 n1
与 n2
。
最后查找 nums
中出现过的数字为 n2
,否则其为 n1
。
class Solution {
public int[] findErrorNums(int[] nums) {
int n = nums.length;
//计算 n1 ^ n2
int xor = 0;
for (int num : nums) {
xor ^= num;
}
for (int i = 1; i <= n; i++) {
xor ^= i;
}
//取 n1 与 n2 二进制表示中一个不相同的位
int lowbit = xor & (-xor);
//将 nums 分成两组
int num1 = 0, num2 = 0;
for (int num : nums) {
if ((num & lowbit) == 0) {
num1 ^= num;
} else {
num2 ^= num;
}
}
//将 1 ~ n 分成两组并异或得到 n1 和 n2
for (int i = 1; i <= n; i++) {
if ((i & lowbit) == 0) {
num1 ^= i;
} else {
num2 ^= i;
}
}
//辨别 n1 和 n2
for (int num : nums) {
if (num == num1) {
return new int[]{num1, num2};
}
}
return new int[]{num2, num1};
}
}