有类似的另外一个题目是:
一个数组中出了一个单独出现的数,其他都是成对出现的,要求找出这个单独出现的数。解决办法是异或每一个数,结果就是这个单独出现的数。
#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; }谢谢~