异或运算法则
- 如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。异或也叫半加运算,其运算法则相当于不带进位的二进制加法:二进制下用1表示真,0表示假,则异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1),这些法则与加法是相同的,只是不带进位,所以 异或常被认作不进位加法。(来源于搜狗百科)
例如,计算 1011101^1000011:
异或性质与扩展
(用不进位相加较好理解)
- 0 ^ N = N
- N ^ N = 0
- 异或运算满足交换律和结合律
c =a ^ b =b ^ a
c =( a ^ b )^ c = a ^ ( b ^ c ) - 不用额外变量交换两个数:
- 一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到这个数?
例如,该数组为 a[]={1, 2, 2, 3, 3}
将所有的数全部异或运算,运算结果就是出现了奇数次的数。 - 一个数组中有两种出现了奇数次,其他数都出现了偶数次,怎么找到这两个数?
例如,该数组为a[]={1,2,4,4,5,5}- 让该数组中所有的数字做异或运算,那么设结果 eor == a ^ b != 0 ;
- 因为eor 不为 0 ,则可以假设 a 与 b在某位上,比如在第三位上,a第三位是1,则b的第三位为0;
- 在其余的出现偶数次的数字中,找出所有在第三位为1的数;
- 用变量 eor’ 与这个数组中所有第三位为1的数做异或运算 ,则 eor’ 最终的答案为a,因为所有第三位为1的数字,除了a,其余为偶数个,异或运算后为0。(因为除了a 与 b ,其余数字的个数都为偶数个,那么可以确定第三位为1的和第三位为0的个数都为偶数个。因为 eor = a ^ b,且其余偶数运算之后结果为0,如果第三位为1的数字个数为奇数,那么第三位为0的数字个数也为奇数,那么将他们全部进行异或运算后,第三位数字为1,不为0,与实际不符。)
- 再用eor与eor’做异或运算,即a ^ b ^ a = b, 再计算 eor ^ eor’ ^ eor ,即 b ^ a ^ b = a;
以下给出该题解的代码:首先了解如何取到该数最右端的1,就是该数取反加一再与该数 (核心:int rightOne = eor & ( ~ eor + 1 );
)
public static void printOddTimesNum(int[] arr){
int eor=0;
for(int i=0;i<arr.length;i++)
eor^=arr[i];
//提取出最右端的1
int rightOne=eor&(~eor+1);
int onlyOne=0; //eor'
//找出这个数组中所有在最右端为1的数字,做异或运算
for(int cur:arr){
if((cur&rightOne)!=0)
//说明该数当前位上的数字也是1
{
onlyOne^=cur;
}
}
System.out.println(onlyOne+" "+(eor^onlyOne));
}