839 字
4 分钟
【算法笔记】(十)异或运算的骚操作
异或运算的性质
- 异或运算就是无进位相加
- 异或运算满足交换律、结合律,也就是同一批数字,不管异或顺序是什么,最终结果都是一样的
0 ^ n = n,n ^ n = 0- 整体异或和如果是
x,整体中某个部分的异或和如果是y,那么剩下部分的异或和是x ^ y
这些结论里最重要的是第 1 条,其他结论都可以由它推导得到。
其中第 4 条相关题目最多,核心在于利用区间上异或和的性质。
1. 不用任何判断语句和比较操作,返回两个数的最大值
// 必须保证n一定是0或者1// 0变1,1变0public static int flip(int n) { return n ^ 1;}
// 非负数返回1// 负数返回0public static int sign(int n) { return flip(n >>> 31);}
// 有溢出风险的实现public static int getMax1(int a, int b) { int c = a - b; // c非负,returnA -> 1 // c非负,returnB -> 0 // c负数,returnA -> 0 // c负数,returnB -> 1 int returnA = sign(c); int returnB = flip(returnA); return a * returnA + b * returnB;}
// 没有任何问题的实现public static int getMax2(int a, int b) { // c可能是溢出的 int c = a - b; // a的符号 int sa = sign(a); // b的符号 int sb = sign(b); // c的符号 int sc = sign(c); // 判断A和B,符号是不是不一样,如果不一样diffAB=1,如果一样diffAB=0 int diffAB = sa ^ sb; // 判断A和B,符号是不是一样,如果一样sameAB=1,如果不一样sameAB=0 int sameAB = flip(diffAB); int returnA = diffAB * sa + sameAB * sc; int returnB = flip(returnA); return a * returnA + b * returnB;}2. 找到缺失的数字
题目链接:https://leetcode.cn/problems/missing-number/
public static int missingNumber(int[] nums) { int eorAll = 0, eorHas = 0; for (int i = 0; i < nums.length; i++) { eorAll ^= i; eorHas ^= nums[i]; } eorAll ^= nums.length; return eorAll ^ eorHas;}3. 数组中有 2 种数出现了奇数次,其他的数都出现了偶数次,返回这 2 种出现了奇数次的数
题目链接:https://leetcode.cn/problems/single-number-iii/
public static int[] singleNumber(int[] nums) { int eor1 = 0; for (int num : nums) { // nums中有2种数a、b出现了奇数次,其他的数都出现了偶数次 eor1 ^= num; } // eor1 : a ^ b
// Brian Kernighan算法 // 提取出二进制里最右侧的1 int rightOne = eor1 & (-eor1);
int eor2 = 0; for (int num : nums) { if ((num & rightOne) == 0) { eor2 ^= num; } } return new int[] { eor2, eor1 ^ eor2 };}4. 数组中只有 1 种数出现次数少于 m 次,其他数都出现了 m 次,返回出现次数小于 m 次的那种数
题目链接:https://leetcode.cn/problems/single-number-ii/
public static int singleNumber(int[] nums) { return find(nums, 3);}
// 更通用的方法// 已知数组中只有1种数出现次数少于m次,其他数都出现了m次// 返回出现次数小于m次的那种数public static int find(int[] arr, int m) { // cnts[0] : 0位上有多少个1 // cnts[i] : i位上有多少个1 // cnts[31] : 31位上有多少个1 int[] cnts = new int[32]; for (int num : arr) { for (int i = 0; i < 32; i++) { cnts[i] += (num >> i) & 1; } } int ans = 0; for (int i = 0; i < 32; i++) { if (cnts[i] % m != 0) { ans |= 1 << i; } } return ans;}部分信息可能已经过时









