一、认识异或运算
1、异或运算
相同为0,不同为1
2、同或运算
相同为1,不同为0
3、异或记成无进位相加!
二、例子
1、6 ^ 7 = ?
解答:110 ^ 111 = 001
三、异或运算的性质
1、0 ^ N == N
2、N ^ N == 0
3、异或运算满足交换律和结合律
上面两个性质用无进位相加来理解就非常的容易
4、有abcde
不用管abcde是什么顺序,只要同一批数,异或的结果是一样的
异或运算和异或的顺序无关
四、测试题
1、题目一:如何不用额外变量交换两个数
int a = 甲
int b = 乙
a = a ^ b
b = a ^ b
a = a ^ b
分析:
第一步运算后,a = 甲 ^ 乙,b = 乙
第二步运算后,a = 甲 ^ 乙,b = 甲
第三步运算后,a = 甲 ^ 乙 ^ 甲,b = 甲
所以,a = 乙,b = 甲
但是字符串是不能用异或操作的,所以只能用于数值型数据进行交换
2、题目二:一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
将所有数异或到一起,留下的数就是奇数次的数
// arr中,只有一种数,出现奇数次
public static void printOddTimesNum1(int[] arr) {
int eor = 0;
for (int i=0; i<arr.length; i++) {
eor ^= arr[i];
}
System.out.println(eor);
}
3、题目三:怎么把一个int类型的数,提取出最右侧的1
比如:
int i = N
二进制形式:001101010000
我想得到一个结果:除了最右侧1有值外,剩下的位都变成0,就是000000010000
方法:
N & (~N + 1),N与上N取反加1
(1)第一步
~N = 110010101111
(2)第二步
~N+1 = 110010110000
(3)第三步
N & (~N+1) = 000000010000
4、题目四:一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数
(1)第一步
假设数组中,a、b出现了奇数次,其他数都出现了偶数次
所有数异或起来的结果eor = a ^ b
但是怎么分开a和b呢?
eor不等于0
eor一定有一位是1
假设第8位是1,说明a的第8位和b的第8位不同
整个数组可以分为两大类:第一类是第8位是1的数,第二类是第8位是0的数
第8位上是1的数可能存在出现了偶数次的数
第8位上是0的数也可能存在出现了偶数次的数
但是a和b一定是分开的
最后把两个组分组异或!!!
// arr中,有两种数,出现奇数次
public static void printOddTimesNum2(int[] arr) {
int eor = 0;
for (int i=0; i<arr.length; i++) {
eor ^= arr[i];
}
// eor = a ^ b
// eor != 0
// eor必然有一个位置上是1
int rightOne = eor & (~eor + 1); //获取最右侧的1
int onlyOne = 0; //eor'
for (int i=0; i<arr.length; i++) {
if ((arr[i] & rightOne) != 0) { //分组异或所有这一位是1的数
onlyOne ^= arr[i];
}
}
System.out.println(onlyOne + " " + (eor ^ onlyOne)); //onlyOne是其中一个数,eor是两个数异或结果
}
5、题目五:获取一个数二进制1的个数
先拿原数求出第一个最右边为1的数,拿这个数和原数异或消掉。重复上述操作,每求出一个最右侧为1的数计数一次
//获取一个数二进制1的个数
public static int bit1counts(int n) {
int count = 0;
while(n !=0) {
int rightOne = n & ((~n) + 1);
count++;
n = n ^ rightOne;
}
return count;
}