mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
839 字
4 分钟
【算法笔记】(十)异或运算的骚操作
2026-03-12
统计加载中...

异或运算的性质#

  1. 异或运算就是无进位相加
  2. 异或运算满足交换律、结合律,也就是同一批数字,不管异或顺序是什么,最终结果都是一样的
  3. 0 ^ n = nn ^ n = 0
  4. 整体异或和如果是 x,整体中某个部分的异或和如果是 y,那么剩下部分的异或和是 x ^ y

这些结论里最重要的是第 1 条,其他结论都可以由它推导得到。
其中第 4 条相关题目最多,核心在于利用区间上异或和的性质


1. 不用任何判断语句和比较操作,返回两个数的最大值#

// 必须保证n一定是0或者1
// 0变1,1变0
public static int flip(int n) {
return n ^ 1;
}
// 非负数返回1
// 负数返回0
public 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;
}
【算法笔记】(十)异或运算的骚操作
http://hgqwd.icu/posts/suanfa10/
作者
天线宝宝死于谋杀
发布于
2026-03-12
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

封面
Sample Song
Sample Artist
封面
Sample Song
Sample Artist
0:00 / 0:00