目录
1.什么是异或运算
异或,是一个数学运算符
如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
同时异或运算满足结合律,交换律,例如:a⊕(b⊕c)=(a⊕b)⊕c
异或也叫半加运算,其运算法则相当于不带进位的二进制加法:
二进制下用1表示真,0表示假,则异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0
2.异或运算来交换数值
有了以上基础,我们就可以使用异或运算来交换数值
1.普通交换
可能大家平时交换数值都是用一个新建temp来保存某个数的值
void swap(int i, int j) {
int temp;
temp=i;
i = j;
j = temp;
}
2.异或交换
接下来的交换是用异或运算来实现的,并不需要新建一个temp,这使得空间没有造成浪费,算法更加优化
void swap(int i, int j) {
i = i ^ j;
j = i ^ j;
i = i ^ j;
}
让我们来看看
- i=i^j,第一步先不管
- j=i^j ,我们将第一步的式子代进去,利用异或运算的性质,j跟j异或等0,然后i跟0异或就等于i,所以j=i。
- i=i^j,我们将第二部j的式子代进去,原理跟第二步相同。
3.经典面试题: 数组里有一种数只出现奇数次,其他都是偶数次,请找出出现奇数次的数
void ARRAY(int arr[]) {
int eor = 0;
for (int i = 0; i < arr.length-1; i++)
eor ^= arr[i];
}
题目说了,只有一个数是出现奇数次,那其他出现偶数次的数都会在异或运算的时候等于0,0跟任何数异或运算后就等于任何数
4. 经典面试题:数组里有两种数出现奇数次,其他都是偶数次,请找出出现奇数次的数
void ARRAY(int arr[]) {
int eor = 0;
for (int i = 0; i < arr.length; i++) {
eor ^= arr[i]; //现在eor是两个出现奇数次的数的异或,假如两个数为6和7
}
int rightOne = eor & (~eor + 1); //提取出eor最右边的1
int onlyone = 0;
for (int i = 0; i < arr.length; i++) {
if ((arr[i]&rightOne)==1) //将6跟7分成两个部分,onlyone跟某个部分异或就会得到6或者7
onlyone ^= arr[i];
}
cout << onlyone << "\t" << (eor ^ onlyone);//onlyone是一个出现奇数次的数,eor ^ onlyone是另一个出现奇数次的数
1. int rightOne = eor & (~eor + 1); //提取出eor最右边的1
可能大家不理解这一步是干什么的
eor是两个出现奇数次的数的异或,假如两个数为6和7
因为1的话肯定是1跟0异或,6的二进制是00000110,7是00000111
依次为标准,将6跟7分开
(arr[i]&rightOne)==1 可以将数组分为两个部分
一个部分有6,一个部分有7,然后其中的出现偶数次的数不变
分为两个部分后,就一直跟一个部分的数异或,就可以得到6或7,
最终在利用(eor ^ onlyone)可以得到另一个数
如果我表述的不够清楚,欢迎大家留言