题目链接:645.错误的集合
数学解法
这个解法主要用到set集合性质,存储的元素不重复,与1~n集合相减,从而找到缺失元素,
对于重复元素 = 原数组 - set集合(set集合中少一个重复元素)
class Solution {
public int[] findErrorNums(int[] nums) {
Set set=new HashSet();
int sums=0;
for (int i = 0; i < nums.length; i++) {
set.add(nums[i]);
sums+=nums[i];
}
int sum_set=0,sum_num=0;
for (int i = 1; i <= nums.length; i++) {
sum_num+=i;
}
Object[] arr_set=set.toArray();
for (int i = 0; i < set.size(); i++) {
sum_set+=(int)arr_set[i];
}
int[] a=new int[2];
a[0]=sums-sum_set;
a[1]=sum_num-sum_set;
return a;
}
}
异或解法
dup:重复元素;mis:缺失元素;
异或1~n和nums每一项,得到dup^mis,相当于mis异或一次,dup异或三次,其余元素异或两次为0,0 ^ a=a;所以得到num = dup^mis
下面要对num进行拆分:
首先我们分析num的值,为dup和mis的不同位构成,通过 num^(-num) = lowbit,提取不同位里第一次出现不同的位置,将最靠右的1作为鉴别点,由此将nums中的元素分为两组num1,num2(dup和mis肯定在不同组中)
用(nums[i] & lowbit)== 0,作为条件,对两组元素都异或
对num1,num2再次for循环异或,范围1~n,条件不变,此时所得num1,num2,其中一个是dup,一个是mis,但不知道谁是谁
为什么两遍分组异或就能得到,分析num1数组,异或可以理解为1^ 2^ 3^ 4 ^ 5^……,它其实变相保存了这一组数据,第二遍异或完整的序列,那么之前的元素遇到相同的元素异或就为0,即舍去了,最终会剩下一个元素这个元素只能是重复或缺失的(即:0 ^ dup=num1或num2 ),对另一组num2也是这个道理,
最后判断num1 num2,分别是dup还是mis,dup在nums中
int* findErrorNums(int* nums, int numsSize, int* returnSize) {
int dup_mis = 0;
for (int i = 1; i <= numsSize; i++) {
dup_mis ^= i;
dup_mis ^= nums[i - 1];
}//两遍异或得到 重复^缺失
//分离这个异或结果dup_mis
int lowbit = dup_mis & (-dup_mis);//提取不同位里第一次出现不同的位置
int num1 = 0, num2 = 0;
for (int i = 0; i < numsSize; i++) {
if ((nums[i] & lowbit) == 0) {
num1 ^= nums[i];
} else {
num2 ^= nums[i];
}
}
for (int i = 1; i <= numsSize; i++) {
if ((i & lowbit) == 0) {
num1 ^= i;
} else {
num2 ^= i;
}
}
int* a=(int*)malloc(sizeof(int)*2);
*returnSize = 2;
for (int i = 0; i < numsSize; i++) {
if (nums[i] == num1) {
a[0] = num1, a[1] = num2;
return a;
}
}
a[0] = num2, a[1] = num1;
return a;
}