认识异或运算
异或运算:相同为0,不同为1
同或运算:相同为1,不同为9
能长时间记住的概率接近0%
所以,异或运算就继承无进位相加!
异或运算的性质
1)0^N = N (用无进位相加来理解更容易记住)
2)N^N = 0(用无进位相加来理解更容易记住)
3)异或运算满足交换律和结合律(记住这句话,就是同一批数不用管顺序,异或起来的结果都是一样的)
上面的两个性质用无仅为相加来理解就非常容易
N & ((~N) + 1)
怎么把一个int类型的数,提取出最右侧的1来(其余所有位置都为0)?这是一个常规操作,需要记住。原因看截图:
题目
例1:一个数组中只有一种数出现了奇数次,其他数都出现了偶数次,怎么找到这个数并打印
public static int findNumber(int[] arr) {
int eor = 0;
for (int i : arr) {
eor = eor ^ i;
}
return eor;
}
例2:一个数组中有两种数出现了奇数次,其他数出现了偶数次,怎么找到并打印这两种数
我们还是准备一个 eor,在 arr 中不妨设 a 和 b 出现了奇数次,其他数都出现了偶数次。那么我把所有数异或起来的结果一定为一个a异或一个b:eor = a^b。因为剩下的偶数个数都不干扰,都抵消掉了。而因为 a 和 b 不相等,所以 eor 肯定不等于0,也就是说名 eor 一定有位置上有1。假设最右边那个为1的位置为第8位,则 a 的第八位和 b 的第八位一定是不同的。所以整个数组就可以分为两大类,一类为第八位为0的,一类为第八位为1的,并且 a 和 b 肯定在两个不同的类别中。再准备一个 eor’,然后对其中一个类别进行例1中的处理,得到 a,那么 b = eor ^ eor’。然后我们只需要把第八位改成最右边为1的那位,就OK。
public static void printOddTimesNum2(int[] arr) {
int eor = 0;
for (int i=0; i<arr.length; i++) {
eor ^= arr[i];
}
int rightOne = eor & ((~eor) + 1);
int a = 0;
for (int i=0; i<arr.length; i++) {
if (arr[i] & rightOne != 0) {
^= arr[i];
}
}
System.out.print("a:" + a + " b:" + a^eor);
}