异或 ^ ,相同为0,不同为1.通过这个性质,我们能得出许多巧妙的解决问题的办法.
异或的一些性质:
A^A=0;
0^A=0;
A^B^A=B;(满足交换律)
应用:
1. 快速判断两数是否相等
也就是用性质1,A==B?改成A^B?就行.
2. 不用第三个变量交换两个数
我记到这道题曾经有人考过我,当时我的方法是:
A=A+B;
B=A-B;
A=A-B;
一直以为自己的答案蛮好的.后来发现了异或版本.
A=A^B;
B=A^B;
A=A^B;
上面的式子综合一下就是:
B=A^B^B=A;
A=A^B^A^B^B=B;
完成了交换.
3. 特定位翻转
0^1=1;
1^1=0;
也就是任意数^1都会翻转.
利用这个性质,可以实现任意位的翻转.如:
10000101
^00001100
=10001001
实现了3,4位的翻转.
4. 找出非成对的数
一大串数,成双成对,只有一个落单.如果快速揪出来?
根据性质1,异或所有数,所有成双成对的都挂掉了.我们就可以在一大串数中找到落单的.
A^B^C^D^C^B^A=D.被揪出来了.
5. 上面问题做一个扩展,如果有两个不成对的如何,如下面序列:
A A B B C D E E.
全部异或,得到C^D.
我们再根据C^D的性质做文章
如果C^D=10000010,说明C^D的最高位不同,一个是0,一个是1.那么我们根据最高位,把原来序列划分为两段:
最高位1:A A E E C
最高位 0:B B D
答案很明显了,分别异或上面两个序列,得到C和D.
这个方法我打算编码出来.
上面方法引申到一个问题,如果快速判断某个数哪些位是1?
暂时用一个比较笨的方法,用>>遍历判断最低位.
还有一个问题,如何快速判断某个数第i位是什么?
想到了异或的一个兄弟&.
A&0=0;
11111111
&0000010
=00000010
上面的式子表示,通过&,我们可以快速找出某位的值.最后编码如下:
- #include<iostream>
- using namespace std;
- #define maxn 10
- void swap(int &a,int &b)
- {
- a=a^b;
- b=a^b;
- a=a^b;
- return;//现学现用
- }
- int main()
- {
- int temp_1;
- int A[maxn]= {1,1,2,2,3,4,5,5,6,6};
- for(int i=0; i<maxn; i++)
- temp_1^=A[i];//总的异或
- int pos=1;//标记不同位
- for(int i=1; i<=8; i++)
- {
- if(temp_1&1)
- {
- pos=pos<<i-1;
- break;
- }
- }
- int L=0;
- int R=maxn-1;//用快排的方法,把序列分为两半
- do
- {
- do
- {
- L++;
- }
- while((A[L]&pos)&&L<maxn-1);
- do
- {
- R--;
- }
- while(!(A[R]&pos)&&R>0);
- if(L<R)swap(A[L],A[R]);
- }
- while(L<R);
- int temp_2=0;
- int temp_3=0;//两个子序列的异或
- for(int i=0; i<=R; i++)
- {
- temp_2^=A[i];
- }
- for(int i=L; i<maxn; i++)
- {
- temp_3^=A[i];
- }
- cout<<temp_3<<' '<<temp_2<<endl;
- return 0;
- }
6. 从1到10000有10001个数,无序存放,查找两个重复数。
现在是一个重复,其他不重复.
我们只要与1-10000的所有数再异或一次,除了查找的数,其他都是偶数次异或,可以得到结果。