异或运算
1. 简介
异或运算XOR 是 exclusive OR 的缩写。英语的 exclusive 意思是"专有的,独有的",可以理解为 XOR 是更单纯的 OR 运算。
我们知道,OR 运算的运算子有两种情况,计算结果为true
-
一个为 true,另一个为 false;
-
两个都为 true。
上面两种情况,有时候需要明确区分,所以引入了 XOR
XOR 排除了第二种情况,只有第一种情况(一个运算子为true
,另一个为false
)才会返回 true,所以可以看成是更单纯的 OR 运算。也就是说, XOR 主要用来判断两个值是否不同:
- 相同为True
- 不同为False
2. 性质
- 一个值与自身的运算,总是为 false
- 一个值与 0 的运算,总是等于其本身
- 满足交换律和结合律
3. 应用
3.1. 简化计算
运用异或运算的性质进行计算简化
3.2. 交换值
利用异或运算的性质,实现无空间开销的交换数值:
private 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];
}
3.3. 加密
-
明文(text)与密钥(key)进行异或运算,可以得到密文(cipherText)
-
密文与密钥再次进行异或运算,就可以还原成明文
3.4. 数据备份
- 文件 x 和文件 y 进行异或运算,产生一个备份文件 z
- 以后,无论是文件 x 或文件 y 损坏,只要不是两个原始文件同时损坏,就能根据另一个文件和备份文件,进行还原
4. 解决问题
4.1. 寻找仅出现一次的一个数
情景:某数列中只有一个数出现了一次,求该数
思路:按照异或运算的性质,亦或运算满足交换律和结合律,偶数次相同的数亦或的结果为0,0亦或任何数等于任何数,所以只需要将数列全部亦或运算即可
public int onlyOnceNum(int[] nums) {
int tmp = 0;
for(int i=0;i<nums.length;i++){
tmp = tmp^nums[i];
}
return tmp;
}
4.2. 寻找仅出现一次的两个数
情景:某数列中只有两个数出现了一次,求该两数
思路:按照前一题的思路,将所有数亦或运算得到这两数的亦或结果,由于两数一定不同,所以在二进制表示法上一定有的位数是1,按照两数亦或结果为1的位数是否为1划分数列中的数,得到分别包含两数的两个子数列,将其中一个数列全部亦或,得到该数列中的这个数,与之前两数亦或的结果再亦或一次得到另一个数
public int[] onlyOnceTwoNums(int[] nums) {
int tmp = 0;
for(int i=0;i<nums.length;i++){
tmp = tmp^nums[i];
}
//获得二进制最右边的1
int rightOne = tmp & (~tmp + 1);
int tmp2 = 0;
for(int i=0;i<nums.length;i++){
if ((nums[i] & rightOne) == 0)
tmp2 = tmp2^nums[i];
}
return new int[]{tmp^tmp2, tmp2};
}
4.3. 寻找缺失的数
情景:在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,求该数
思路:将所有数亦或运算再与0~n-1内的所有数亦或运算即可得到该数
public int missingNumber(int[] nums) {
int tmp = 0;
for(int i=0;i<nums.length;i++){
tmp = tmp^i^nums[i];
}
return tmp^(nums.length);
}
5. 参考资料
[1]异或运算 XOR 教程 - 阮一峰的网络日志 (ruanyifeng.com)
[2]剑指 Offer 53 - II. 0~n-1中缺失的数字 - 力扣(LeetCode) (leetcode-cn.com)