异或(^)
- 基本操作: 相同出0,不同出1
- 另一个说法:无进位进行相加
比如
101011
^ 110101
= 011110
这里就是无进位的相加不需要管进位的问题
即直接相加不需要关系进位的问题
特点
1、 N ^ 0 = N (任何数异或上0都是自己) 可以用无进位相加来进行理解
2、N ^ N = 0 (因为二进制是一样的,相同出0)
3、异或符合 交换律 和 结合律
a ^ b = b ^ a
(a ^ b) ^ c = a ^ (b ^ c)
冒泡排序中使用异或的方式交换数据的方式
public static void swap(int[] arr, int i, int j) {
// 使用异或的方式进行交换
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
解析:
arr[i] = "A";
arr[j] = "B";
(1) arr[i] = arr[i] ^ arr[j]; arr[i] = "A" ^ "B";
(2) arr[j] = arr[i] ^ arr[j]; arr[j] = "A" ^ "B" ^ "B" ==> "A" ^ 0 ==> "A"
(3) arr[i] = arr[i] ^ arr[j]; arr[i] = "A" + "B" + "A" ==> "0" + "B" ==> "B"
所有最后 : arr[i] = "B"; arr[j] = "A";
注意
使用这个异或交换数据的时候,交换的数据一定不能在内存的同一片空间之中,不然数据会被清空
所以必须保证交换的数据必须在两个不相同的内存空间中
使用异或解决的二个算法题
有一堆数,里面有一个数的个数是奇数,其他的都是偶数
思路
采用异或的方式进行解决问题,保证时间复杂度为 O(n) 空间复杂度为 O(1)
public static void printOddNum1(int[] arr) {
// 让这个数和所有的数进行异或
int eor = 0;
for (int num : arr) {
eor ^= num;
}
// 最后出来就是那个奇数个的数
System.out.println(eor);
}
原因
可以怎么写的原因:
1、因为无论异或的顺序是什么样子的,最后的结果都是一致的
2、当两个相同的数进行异或,结果就是0,当0和一个数进行异或,结果就是这个数
3、所有异或到最后只有的那个数就是那个奇数
有一堆数,里面有两个数的个数是奇数,其他的都是偶数(进阶)
思路:
1、先进行异或处理,将所有的数进行异或,得到的值就是 两个奇数进行异或的值
2、因为这两个值是不相同的,所以肯定得到的值里面有一个位置为1
3、找到这个1的位置,再将这个位置为1的值进行异或,得到的值就是其中的一个
4、再将这个值和之前和所以值进行异或得到的值进行异或,得到的就是另一个值
public static void printOddNum2(int[] arr) {
// 让这个数和所有的数进行异或
// 假如 第一个奇数为 a 第二个奇数为 b
int eor = 0;
for (int num : arr) {
eor ^= num;
}
// eor = a ^ b
// 找到最右边的1的位置 异或不同才出1
// 00000100
int rightNum = eor & (~eor + 1);
// 将这个位置为1的数进行异或
int eorOpen = 0;
for (int num : arr) {
// == 0 说明了在rightNum这个位置为 0
if ((num & rightNum) == 0) {
eorOpen ^= num;
}
}
// eorOpen 不是 a 就是 b
// 所以另一个数就是 eorOpen ^ eor
System.out.println(eorOpen + " " + (eorOpen ^ eor));
}