1.
示例 1 :
输入:nums = [2,2,1]
输出:1
示例 2 :
输入:nums = [4,1,2,1,2]
输出:4
示例 3 :
输入:nums = [1]
输出:1
在重复数字的题目中,有两个很重要的结论:
1.两个相同的数字相异或结果是0(异或是指两个数字的二进制位相同为0,不同为1)
2.0和任何数字异或都是那个数字
所以第一道题,可以直接让数组所有的元素异或,得到的结果就是只出现过一次的数字:
int singleNumber(int* nums, int numsSize) {
int result=0;
for(int i=0;i<numsSize;i++){
result=nums[i]^result;
}
return result;
}
2.
示例 1:
输入:nums = [2,2,3,2]
输出:3
示例 2:
输入:nums = [0,1,0,1,0,1,100]
输出:100
第二题没有想到简单的方法,还没研究答案的做法,是暴力求解的,思路大概是:把数组进行冒泡排序,如果一个元素既不等于左边的数也不等于右边的数,那么这个元素就是只出现一次的数。
int singleNumber(int* nums, int numsSize){
int tmp=0;
//如果数组只有一个元素,那它就是只出现一次的数
if(numsSize<4){
return nums[0];
}
//冒泡排序
for(int i=0;i<numsSize;i++){
for(int j =0;j<numsSize-i-1;j++){
if(nums[j]>nums[j+1]){
tmp=nums[j];
nums[j]=nums[j+1];
nums[j+1]=tmp;
}
}
}
//检查只出现一次的数是否在数组的左右边界,防止越界访问
if(nums[0]!=nums[1]){
return nums[0];
}else if(nums[numsSize-1]!=nums[numsSize-2]){
return nums[numsSize-1];
}
//判断一个数和左右的数相不相等
for(tmp=1;tmp<numsSize-2;tmp++){
if(nums[tmp]!=nums[tmp-1]&&nums[tmp]!=nums[tmp+1]){
return nums[tmp];
}
}
return 0;
}
需要注意的是:1.如果数组只有一个元素,直接返回[0],2.对下标是[1]~[numsSize-2]的元素进行for循环判断是否和左右想等,下标是[0]和[numsSize-1]的需要单独判断一边。
3.
示例 1:
输入:nums = [1,2,1,3,2,5]
输出:[3,5]
解释:[5, 3] 也是有效的答案。
示例 2:
输入:nums = [-1,0]
输出:[-1,0]
示例 3:
输入:nums = [0,1]
输出:[1,0]
同样运用异或的结论:但是有一个问题,现在有两个只出现一次的数,直接异或最后肯定不会得到这两个数,所以我们要把两个数分开,所以对于只出现一次的两个数,可以通过寻找这两个数的二进制的不同位来把两个数分开(因为两个数不相等,一定有一位二进制位不相同),进而把两个数分开。对于出现两次的两个数,它们的二进制位一模一样,所以分到一个组。
现在我们就得到了两组数,两组的数都和《只出现一次的数字1》一样,进而每组数异或得到两组中只出现一次的数,也就是答案。
这里拿题目的实例1举例:
如图所示,因为3,5 第二个二进制位不同,所以根据这一位来分类,0的一类,1的一类,
最后这两组异或处理最后得到答案。
int* singleNumber(int* nums, int numsSize, int* returnSize) {
int number=0;
int result=0;
*returnSize=2;
int num1=0,num2=0;
for(number=0;number<numsSize;number++){
result =result^nums[number];
}
for(number=0;number<32;number++){
if((result>>number)&(1)==1){
break;
}
}
for(int i =0;i<numsSize;i++){
if((nums[i]>>number)&(1)==1){
num1=nums[i]^num1;
}else{
num2=num2^nums[i];
}
}
int* array=malloc(2*sizeof(int));
array[0]=num1;
array[1]=num2;
return array;
}
这里再解释一下前两个for循环,最后的for循环就是两组数的一直异或比较好理解。
第一个for循环的目的是得到两个只出现一次的数的异或值(相同的数异或完都变成0了,0接着异或不影响),第二个就是找到这两个只出现一次的数的二进制位哪一位不同,(按位于1,如果结果为1说明这个二进制位不同)并且根据这个来分组。
感谢阅读,希望文章对你有所启发,如有错误欢迎评论。