抑或的定义是,只有当两个二进制数上的对应位相同时为0,相异时为1。这就让按位抑或具有了这样的功能,即让两个相同的数字抑或后的结果为0,如果是多个数字,只要这些数字两两相同,那么在集体抑或后,无论抑或过程中的顺序如何,都只会影响抑或的过程,但不会影响最终结果,即集体抑或的结果一定为0。
1,面试题 17.04. 消失的数字 - 力扣(LeetCode) (leetcode-cn.com)
本题当然可以采用将数字从1到n相加,再减去数组中的所有元素的做法,但这里介绍抑或方法。(抑或:均为1,则为0,其余情况均不变)两个相同的数字相抑或,结果一定为0,同样道理,多对两两相同的数字,集体进行抑或,最终结果一定为0,哪怕是数字之间打乱顺序,会改变过程,但不会影响最终结果。如果在这些两两相同的数字之间,有一个只出现一次的数字,那最终抑或的结果一定等于该数字。
int missingNumber(int* nums, int numsSize)
{
int x=0;
for(int i=0;i<numsSize+1;i++)
{
x^=i;
}
for(int j=0;j<numsSize;j++)
{
x^=nums[j];
}
return x;
}
2,136. 只出现一次的数字 - 力扣(LeetCode) (leetcode-cn.com)
int singleNumber(int* nums, int numsSize)
{
int i = 0;
int result=0;
for(i=0;i<numsSize;i++)
{
result=result^nums[i];
}
return result;
}
3,剑指 Offer 56 - I. 数组中数字出现的次数 - 力扣(LeetCode) (leetcode-cn.com)
数组中除了两个不同的数字之外,其余的数字均出现两两出现。要求找出单独出现的两个数字。本题与前面的题目不同之处在于,这道题需要找两个数字,而根据数字集体抑或的结果是找不出两个数字的。
求解本题的思路仍然是将所有的数字集体抑或,抑或后的结果一定等于单独出现的两个数字抑或的结果。虽然根据该结果不能求出这两个数字,但是可以从该结果身上看出规律。根据抑或的定义,如果抑或的结果上有一位上的数字为1,那么说明原始的两个数字上该位上一定分别为0,1。依据这一位上的值,就可以将所有的元素分为两组,分组后的结果一定是两个目标数字分处两组,而两个数组中的其余数字一定分别两两出现。这时可以继续在两个数组中分别抑或,得到的结果就是想要找的那两个值。
int* singleNumbers(int* nums, int numsSize, int* returnSize)
{
int ret=0;
for(int i=0;i<numsSize;i++)
{
ret^=nums[i];
}//先定义一个变量ret,记录数组中所有数字集体抑或后的结果,根据题意,数组中除了两个数字之外,其余数字均两两一对的出现,那么当所有数字抑或完之后,最后的结果自然而然就是单独出现的那两个数字抑或的结果
int j=0;
for(j=0;j<numsSize;j++)
{
if(ret%2==1)
{
break;
}
ret/=2;
}//在上面的代码中得到了单独出现的两个数字抑或的结果,通过该结果无法反推出这两个数字,但是这个结果可以体现规律,根据抑或的规则,如果二进制数的某一位上抑或的结果为1,那说明原先的两位上一定是一个为0,一个为1,据此,就找到了寻找的两个目标值的特点,即该位上一个为0,一个为1
int x=0,y=0;
for(int k=0;k<numsSize;k++)
{
if((nums[k]>>j)&1)//如果一个二进制数向右移动j位之后,并和1进行按位与操作,且结果不为0,说明该位上的数字为1(按位与操作只有两个数字都为1时才为1)
{
x^=nums[k];
}
else
{
y^=nums[k];
}
}//该for循环中要做的事就是根据上面找到的两数字的差别将整个数组的数字分成两组,分组之后的情况是,两个目标数字一定分别出在两个数组中,并且各数组中剩下的数字均为两两成对出现的数字。分好组后就可以将两数组中的元素分别进行集体抑或,根据上面的思想,最终的结果一定是单独出现的两个数字
int*arr=(int*)malloc(sizeof(int)*2);
arr[0]=x;
arr[1]=y;
*returnSize=2;
return arr;
}
4,剑指 Offer 56 - II. 数组中数字出现的次数 II - 力扣(LeetCode) (leetcode-cn.com)
本题中,数组中除了一个元素单独出现之外,剩下的元素均三个相等的数字一组的出现。要求找到那个单独出现的元素。这道题的第一反应当然是使用木桶法解题,但是题中所给的数组元素的取值范围过大,木桶法会消耗大量的时间。
求解本题的思路仍是采用木桶法,但是这次桶中存放的是所有数二进制的某一位上1的总和,由于重复出现的数字都是重复3次出现,因此,最终桶中存放的1的数量一定都是3的倍数,如果不是的话,那该位上多出的那个1就是单独出现的那个数字的。将重复出现的各个元素中的1都去掉之后,就可以根据剩下的各个位上的1还原出该单独出现的数字了。
int singleNumber(int* nums, int numsSize)
{
int arr[32]={0};
for(int i=0;i<numsSize;i++)
{
for(int j=0;j<32;j++)
{
if((nums[i]>>j)&1)
{
arr[j]++;
}
}
}
long int num=0;
for(int i=31;i>=0;i--)
{
arr[i]=arr[i]%3;
num=(num+arr[i])*2;
}
return num/2;
}