1.什么是异或运算
异或运算是位运算的一种,符号为:^
运算规则为:相同为0,不同为1
例如
性质:
N ^ 0 = N
N ^ N = 0
A ^ B = B ^ A
(A ^ B) ^ C = A ^ (B ^ C)
- N ^ 0 = N
public class XorOperation {
public static void main(String[] args) {
System.out.println(zeroXorNonZeroNum(5)); // 5
}
// 0与非零数异或
private static int zeroXorNonZeroNum(int n) {
return 0 ^ n;
}
}
位与位异或,相同为0,不相同为1,所以b的结果为5
- N ^ N = 0
public class XorOperation {
public static void main(String[] args) {
System.out.println(nonZeroXorSelf(5)); // 0
}
// 非零数与自身异或
private static int nonZeroXorSelf(int n) {
return n ^ n;
}
}
两个相同的数,二进制都是一样的,所以在位与位进行异或的时候都相同,每一位的结果都是0
- 交换律
public class XorOperation {
public static void main(String[] args) {
System.out.println(twoNonZeroNumXor(3, 8)); // 11
System.out.println(twoNonZeroNumXor(8, 3)); // 11
}
// 两个非零数异或运算
private static int twoNonZeroNumXor(int a, int b) {
return a ^ b;
}
}
因为异或是位与位进行计算的,所以异或的顺序不重要,结果是一样的
- 结合律
int a = 2;
int b = 5;
int c = 4;
int d = (a ^ b) ^ c;
int e = a ^ b ^ c;
int f = a ^ (b ^ c);
System.out.println(d == e); //true
System.out.println(f == e); //true
通过交换律得知,异或的顺序不会影响结果,所以在某一些数先异或,再异或另个数的时候,也不会影响结果,这就是异或满足结合律
2.异或运算的本质
异或运算的本质是不带进位的加法
2.1 2阶异或运算
来看一个例子:
(10进制)5 = (二进制)00000101 (10进制)6 = (二进制)00000110
5 ^ 6 计算过程如下:
相同为0,不同为1
2.3 3阶异或运算
来看一个例子:
(10进制)5 = (三进制)00000012 (10进制)6 = (三进制)00000020
5 ^ 6 计算过程如下:
2 ^ 0 = 2,1 ^ 2 = 0
2.4 4阶异或运算
来看一个例子:
(10进制)5 = (四进制)00000011 (10进制)6 = (四进制)00000012
5 ^ 6 计算过程如下:
1 ^ 2 = 3,1 ^ 1 = 2
3.异或运算的应用
3.1 交换两个变量的值
代码如下:
public class XorOperation {
public static void main(String[] args) {
int a = 4;
int b = 5;
System.out.println("==== exchange before ====");
System.out.println("a = " + a);
System.out.println("b = " + b);
a = a ^ b;
b = a ^ b;
a = a ^ b;
System.out.println("==== exchange after ====");
System.out.println("a = " + a);
System.out.println("b = " + b);
}
}
运行结果
==== exchange before ====
a = 4
b = 5
==== exchange after ====
a = 5
b = 4
3.2 如何把一个数字x最右侧那个1拿出来,变成00…10…的格式
代码如下
public class XorOperation {
private static int getRightOne4Num(int n) {
return n & (~n + 1);
}
public static void main(String[] args) {
System.out.println(getRightOne4Num(5));
}
}
运行结果
1
举一个例子:
比如取出5的最右侧为1的二进制数字
3.3 统计一个二进制数中1出现的次数
代码如下
public class XorOperation {
public static int countBinaryOne(int n) {
int count = 0;
while (n != 0) {
int rightOne = n & (~n + 1);
count++;
n ^= rightOne;
}
return count;
}
public static void main(String[] args) {
System.out.println(countBinaryOne(5));
}
}
运行结果
2
3.4 在一个数组中,只有一种数,出现奇数次,如何把它找出来
代码如下
public class XorOperation {
// 数组中,只有一种数,出现奇数次
public static void printOddTimesNum1(int[] arr) {
int eor = 0;
for (int i = 0; i < arr.length; i++) {
eor ^= arr[i];
}
System.out.println(eor);
}
public static void main(String[] args) {
printOddTimesNum1(new int[]{1, 2, 1, 1, 4, 5, 4, 5, 2});
}
}
运行结果
1
3.5 在一个数组中,有两种数,出现奇数,如何把这两种数找出来
代码如下:
public class XorOperation {
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);// 取出最右边的1
int onlyOne = 0;
for (int i = 0; i < arr.length; i++) {
if ((arr[i] & rightOne) != 0) {
onlyOne ^= arr[i];
}
}
System.out.println(onlyOne + " " + (eor ^ onlyOne));
}
public static void main(String[] args) {
printOddTimesNum2(new int[]{1, 2, 1, 1, 4, 5, 4, 5});
}
运行结果:
1 2
4.总结
- 异或运算的本质是k阶不带进位的加法运算
- 偶数个k异或运算的结果为0
- 奇数个k异或运算的结果为k本身
- x异或0=x,x异或x=0,异或运算不考虑顺序