一、基础概念
异或^
相同为0,相反为1
从而演化出:
0^N=N
N^N=0
a^b=b^a 满足交换律
(a^b)^c=a^(b^c) 满足结合律
二、应用
1、【&】取最右位1
int rightOne = eor & (~eor +1 ); //提取出最右位的1
主要是利用了~eor右边都是1的话,只要~eor+1进位导致都会为0,而且最右的1会被进位标记为0
eor: 1 0 1 0 1 1 0 0
~eor: 0 1 0 1 0 0 1 1
~eor+1: 0 1 0 1 0 1 0 0
——————————————————————————
rightOne: 0 0 0 0 0 1 0 0
2、【^】交换2个数
int a=7,b=20;
a=a^b;
b=a^b;
a=a^b;
print a,b //a=20,b=7
主要利用异或的交换、结合原则
a=a^b; a=7^20
b=a^b; b=(7^20)^20 则 b=7
a=a^b; a=(7^20)^7 则a=20
当然不用临时变量的还有其它方法
a = a + b; b = a - b; a = a - b;
a = a * b; b = a / b; a = a / b;
2、【^】案例-异常次数1
一个数组int arr[n],n>2里面有一种数
是奇数数量存在,其他是偶数数量存在,请输出该值,要求时间复杂度O(n) ,空间复杂度O(1)。
void printOddtimes1(int arr[],int n)
{
int eor=0;
for(int i=0;i<n;i++)
eor^=arr[i]
cout << eor <<endl;
}
一堆数字互相异或,利用了交换律和结合律,不用理会顺序,最后偶数数量的数字肯定会N^N=0,所以最后的eor就是奇数数量的值。
3、【^】案例-异常次数2
一个数组int arr[n],n>2里面有两种数
是奇数数量存在,其他是偶数数量存在,请输出该值,要求时间复杂度O(n) ,空间复杂度O(1)。
解:
假设这2个数为a,b。参照应用2,能得出eor=a^b,那么怎么分开这2个数,也是能用异或。
首先根据题目这是两种数,那么他肯定不相等,并且不等于0,就可以得出eor这int里面最少有1位(rightOne)是1,我们假设是第3位为1.
由于是eor是a^b出来的,所以这1位一定是0和1,所以在arr里肯定可以分成一半第三位是1的一半第三位是0的。这样其中一半里面,有一个数是奇数数量,其他都是偶数数量(又回到案例-异常次数1)那么只要它们里面互相异或,就能得出a 或者b了。
假设我们得到的是a,那么b=eor^a=(a^b)^a=b
这里有几个难点,上面假设的第三位在代码里面怎么取,取哪位?怎么获得=1的那一半?
void printOddtimes2(int arr[],int n)
{
int eor=0;
for(int i=0;i<n;i++)
eor^=arr[i]
int rightOne = eor & (~eor + 1);//第三位在代码里面怎么取?这里用简便的方法拿到一个1
//而且这里我们选择用该位=1的那一半,因为=0的那一半不好去判断
int onlyOne = 0;
for(int i=0;i<n;i++)
{
if(1==rightOne&arr[i])//获得=1的那一半,这里=0取0的那一半也行,一样的
onlyOne^=arr[i]
}
cout <<eor <<" and "<<eor^onlyOne <<endl;//eor^onlyOne 获得另一个值
}
这里当然就不确定哪个是a,哪个是b了,当然题目也没要求。