异或的定义和特性
1)
运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0;即:参加运算的两个对象,如果两个相应位为“异”(值不同),则该位结果为1,否则为0。
2)满足交换律和结合律,a^b=b^a; a^b^c=a^(b^c);
举个交换的例子:
我们定义了俩个变量:int a=15;int b=10;
一般交换的方式是申请一个中间变量temp,temp=a;a=b,b=temp;
但如果使用异或的话,可以不需要辅存空间
a=a^b;
b=a^b; //b=a^b^b=a;
a=a^b; //a=a^b^a=(a^a)^b=b;
成功实现交换。
接下来用俩个问题来介绍异或的下一个用法
问题一:在一个数组中,一种数出现了奇数次,其他数都出现了偶数次,请找出那个出现了奇数次的数。
问题二:在一个数组中,俩种数出现了奇数次,其他数都出现了偶数次,请找出这俩个出现了奇数次的数。
俩个问题都要求时间复杂度o(n),空间复杂度o(1)
问题一的解法:使用值为0的整形数eor去和数组的每一位进行异或运算,最后得到的就是唯一一个出现次数为奇数次的数。(原理就是异或具有交换律和结合律,出现偶数次的数可以和自身进行异或,然后值为0,0和任何数异或都是那个数本身)
public class numberOne {
public static void main(String[] args) {
//问题一的异或解法
int eor=0;
//我们准备一个数组,
int []arr=new int[] {11,12,13,11,12};
for(int i=0;i<arr.length;i++) {
eor^=arr[i];
}
System.out.println(eor);
eor=0;
int []arr1=new int[] {11,12,13,11,12,13,15,17,17};
for(int i=0;i<arr1.length;i++) {
eor^=arr1[i];
}
System.out.println(eor);
}
}
下面是运行得到的结果,确实交换过来了。
问题二的解法:
大部分解释都在代码的注释部分,这里介绍一下与运算(&)按位与运算符 比如a&b;表示把a和b进行二进制的按位与运算 比如8&10,其中8的二进制是0000 1000,而10的二进制是0000 1010,因此 0000 1000(十进制8) & 0000 1010(10进制10) 结果为0000 1000(就是10进制的8) 因此8&10的结果为8 与的计算规则是,如果两个数都都为真(或为1),其结果为真,如果两位数中有一位为假(或为0)者结果为假。
//问题二,在一个数组中,俩种数出现了奇数次,其他数都出现了偶数次,请找出这俩个出现了奇数次的数。俩个问题都要求时间复杂度o(n),空间复杂度o(1)
public class numberTwo {
public static void main(String[] args) {
int []arr=new int [] {1,2,3,2,3,5,8,5};
questionTwo(arr);
}
public static void questionTwo(int []arr) {
int eor=0,onlyOne=0;
for(int i=0;i<arr.length;i++) {
eor^=arr[i];
}
//eor=a^b;//a和b分别是出现奇数次的数
//eor!=0
//eor必然有一个位置上是1(这里相对的是二进制的表示形式,例如二进制下的0101(5),0100(4),5!=4;5^4=0001=1(!=0);5^5=0000=0)
//只有每一位都相同,即俩个数相同,那么异或下来才是0,那么两个数不相同,那么在二进制表示下至少有一位上这两个数不一样,一个数为1,一个数为0)
//这里介绍用二进制表示数是为了后面好分类,例如可以分成最右一位上是1的数和最右一位上为0的数。
int rightOne=eor&(~eor+1);//提取出最右侧的1;这里的“~”是取反操作,就是二进制数的每一位取反,0变1,1变0;
//原理在这,例如这个数为011010,我们先取反,变成100101,然后再加个1,不就变成了100110
//然后我们再使用&运算,这样得到的数不就是000010吗,取出了最右侧的那个1,这下我们就可以根据这个来分类了
for(int i=0;i<arr.length;i++) {
if((arr[i]&rightOne)==0) {
onlyOne^=arr[i];//这一步是提取出符合我们确定的标志位的数,例如最右一位上为1的数,然后进行异或运算,得到a和b中那个在此标志位上为1的数
}
}
System.out.println(onlyOne+" "+(eor^onlyOne));//eor=a^b,a和b中的一个数赋值给了onlyOne,假如这个数为a,那么eor^onlyOne=a^b^a=b;这样就求得了俩个数
}
}
运行结果如下图:
学算法的时候看到这俩个例题(好像还是某公司的面试题),解法让我眼前一亮,然后加上自己的理解,写了这篇分享,如果解释不当之处,还望大家指正。