有类似的另外一个题目是:
一个数组中出了一个单独出现的数,其他都是成对出现的,要求找出这个单独出现的数。解决办法是异或每一个数,结果就是这个单独出现的数。
#include<stdio.h>
int main()
{
int arr[7]={1,2,3,9,3,1,2};
int i=0;
int size=sizeof(arr)/sizeof(arr[0]);
int res=arr[0];
for(i=0;i<size;i++)
{
res^=arr[i];
}
print("%d\n",res);
return 0;
}
那么这道题也一样,只不过异或的结果是这两个单独出现的数异或的结果,需要做的就是将它们分开就行了。
原理:
一个数异或自身,结果是0
0异或任何数,结果为这个数
Step1:异或整个数组,得出单独出现的两个数异或的结果。
我们可以得出结论——这个结果中为 1 的比特位表示这两个数的二进制序列在这个比特位不同。
比如:0110
1010 ^
-----------------
1100 //不看其他位,至少这两个数在第3个比特位是不同的
Step2:从这个结果中将单独出现的两个数分类出来。
分类的原理就是Step1的结论,
我们需要在结果中查找第一个为1的比特位,需要的是它在32个比特位中所处的位置,假设为N。
可以断言这两个数至少在这个比特位是不同的。
接下来用1左移N位与每个数按位与,结果为1分为一组,否则分为另一组。
分别对每一组使用文首的方法的就可以得出这两个数。
但是!我们的目的不是简单地将它做出来!我们要通过这道题在之前的基础上进一步了解异或,并且要写出
简单明了高效不臃肿的程序,要学会巧妙地优化程序,仔细看程序~。
#include<stdio.h>
int main()
{
int arr[]={1,2,3,4,67,3,4,1,666,2};
int size=sizeof(arr)/sizeof(arr[0]);
int i=0;
//求单独出现的两个数异或的结果
int res=arr[0];
for(i=0;i<size;i++){
res^=arr[i];
}
//在res的二进制序列由低到高找出第一个为1的比特位
//正常情况下,我们是用1位移来进行遍历二进制序列,之后需要另一个变量来接受i(位置)
//那么像下面这样,用一个值为1的变量来查找岂不是很优秀?
int flag=1;
for(i=0;i<32;i++){
if(res&(flag<<i)){
break;
}
}
//在分类的同时异或,大大提高了程序的效率。
//一般情况下,我们首先想到的是将结果分类到两个数组中,然后分别进行分类
//再进一步思考,分类的同时进行异或
//下面这样岂不是更好
int x=0;
int y=0;
for(i=0;i<size;i++){
if(1==arr[i]&flag){
x^=arr[i]
}
else{
y^=arr[i];
}
}
printf("%d,%d\n",x,y);
return 0;
}
谢谢~