前提:
异或操作符合交换律,结合律(因为其根本上来抽象理解,就是查看所有项二进制数相同位是否有奇数个1,对运算结果二进制数而言,没有该位为0,有该位为1,与顺序无关)。
任何数与零进行异或,结果仍是他自己
两个相同的数进行异或操作,结果为零(自反性)
如下实现数值交换代码
public void swap(int[]arr,int x,int y){//用异或运算做交换arr[x]=arr[x]^arr[y];arr[y]=arr[x]^arr[y];arr[x]=arr[x]^arr[y];}
该操作不需要再开辟另一块内存空间去进行数值交换
但是注意交换数值双方指向内存必须是两块独立的内存(相同值没问题,相同内存不行)
如上如果,x,y指向同一块内存,第一次异或使arr[x]指向内存存储的数变为0,与此同时,由于arr[y]与arr[x]指向同一块内存,arr[y]也变为0,那么后面两次异或没有意义,原先存储的数丢失了。
问题解决实例:在一堆数中只有一个数出现了奇数次,查出这个数
对所有数进行异或运算,那么最后的结果将是该出现奇数次的数
public int getTheOneNumber(int[] arr){int number=0;for (int i : arr) {number = number^i;}return number;}
那如果是一堆数中有两个数出现了奇数次,其他都出现了偶数次,如何找出这两个数
这是我们如果依然对这一堆书进行异或运算,那我们将得到这两个数异或的结果
为了方便,我们把这两个数称为x,y, 我们现在得到eor=x^y
x,y一定不相同,那么eor值不为0;
所以,x,y的二进制数一定存在一位或多位,一个为1一个为0的情况
那么我们接下来取出eor最右侧的1(假设该位是第i位)(取数方法eor取反加一在于eor做与运算),所有数与该数做与运算将所有数分为i位上为1的数和i位上为零的数,x,y因此被分开,分开后另使同为1(0)的数异或得到x(y)
x(y)与eor异或得到y(x)
public int[] getTwoNumber(int[] arr){int eor=0;for (int i : arr) {eor^=i;}int number = (~eor+1)&eor;int eor2=0;for (int i : arr) {if((number&i)!=0){eor2^=i;}}eor = eor2^eor;int[] goal = {eor,eor2};return goal;}