应用一:如何不用额外变量交换两个数:
异或运算,保证内存是2个独立的
交换操作:
a = a ^ b;
b = a ^ b;
a = a ^ b;
实际上倒的是2个空间
a = a ^ b; // 此时 a = a^b, b = b
b = a ^ b; // 此时 b = a^b^b = a, a=a^b
a = a ^ b; //此时 a = a^b^a = b, b = a
如果交换发生在同一个内存空间,就会出现问题:
swap(arrs, 0, 0) // 0和0位置交换,就会把 0 位置的值 计算成0
不要这么写,老老实实的写交换
应用二:一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
异或操作满足:交换律 和 结合律
异或 [ 2 1 3 2 2 4 1 3 1 2 4 1 4]
4个1
4个2
2个3
3个4
异或等同于 [ 1 1 1 1 2 2 2 2 3 3 4 4 4]
异或4个1 = 0
异或4个2 = 0
异或2个3 = 0
异或2个4 = 0,最后剩下4
应用三:怎么把一个int类型的数,提取出最右侧的1来
N & ( (~N) +1)
N = 0...0 1 1 0 1 0 1 0 0 0 0~N = 1…1 0 0 1 0 1 0 1 1 1 1
~N+1= 1...1 0 0 1 0 1 1 0 0 0 0再&N= 0…0 0 0 0 0 0 1 0 0 0 0
应用四:一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印两种数
先异或一遍所有数
int eor ^= arrs[i];
假设 a , b 是奇数次出现的数
得到的结果是 eor = a ^ b;
因为 a != b,所以 eor != 0,所以 eor 必有一个位置上是1
假设,第8位上是1
那么,a的第8位,和b的第8位不一样
所以,整个数组可以分为两大类:
第一类:第8位是1的数,也可能包含偶数的数
第二类:第8位是0的数,也可能包含偶数的数
但是,a和b一定不在同一类
随便在eor中找一位即可,所以找最右侧的1就可以
int rightOne = eor & ( (~eor) +1 ); //取到最右侧的1
再准备一个 eor ‘
重新遍历数组,异或,只异或第8位是1的数,第8位出现偶数的数 依然不会干扰,异或之后还是0,只有a或b被eor ‘抓到
所以 eor ‘ = a 或 b
因为 eor = a ^ b
所以另外一个数 = eor ^ eor ‘
a ^ b ^ b = a
a ^ b ^ a = b
应用五:随意给一个数,数出二进制1的个数
public static int bit1counts( int N ){
int count = 0;
while ( N != 0) {
// 011011010000 初始值
// 000000010000 最右侧1
// 011011000000 异或抹掉最右侧1,周而复始的
int rightOne = N & ( (~N) + 1);
count++;
N ^= rightOne; //抹掉最右侧的1
// 注意 这里不能是 N -= rightOne 因为 N是正数可以,负数减的时候不是单纯的把1抹掉的形式
}
}
如果不会这种技巧,只能用for循环32次