一、六种基础运算符
| 符号 | 名称 | 规则 | 典型用途 |
|---|---|---|---|
<< |
左移 | 各二进制位左移,低位补 0 | 1 << x 造第 x 位的掩码 |
>> |
右移(算术) | 右移,高位补符号位 | 取第 x 位的值 |
>>> |
右移(无符号) | 右移,高位一律补 0 | Java 特有,处理带符号数时救命 |
~ |
按位取反 | 0↔1 全反转 | 配合 +1 造负数、造反掩码 |
& |
与 | 同为 1 才 1 | 抹掉某些位 |
| |
或 | 有 1 就 1 | 置上某些位 |
^ |
异或 | 不同才为 1 | 翻转某些位、去重 |
补第一个坑 :
>>和>>>千万别混。对负数>>高位补 1,右移再多次也还是负数;>>>高位补 0,能拿到无符号视角。判断第 x 位时用>>没问题,但若要遍历一个 long 的所有位,必须用>>>。
二、判断二进制第 x 位是 0 还是 1
int bit = (n >> x) & 1;
把第 x 位移到最低位,再用 &1 把其他位全抹掉。
三、把第 x 位改成 1 / 0
置 1(或上一个只有第 x 位为 1 的掩码)
n |= (1 << x);
置 0(与上一个只有第 x 位为 0、其余全 1 的掩码)
n &= ~(1 << x);
补第二个坑 :
&用来清零 ,|用来置位 ,^用来翻转 。很多人写置 0 时下意识也用^,结果一旦该位本来就是 0,反而被翻成 1 了。
四、lowbit:提取最右侧的 1
n & (−n)
原理 :计算机里 -n = ~n + 1。取反后最右那个 1 变成 0、它右边全变 1,再 +1 会一路进位,恰好把那个 1 重新顶回原位,于是原数和它的负数只在最右侧那个 1 处同时为 1。
用你笔记里的 011010100(即 212):
n = 011010100
~n = 100101011
~n + 1 = 100101100
n & -n = 000000100 → 4 ✓
用途:树状数组的核心、以及"只出现一次的数字 III"里拿来分组。
五、干掉最右侧的 1
n & (n−1)
n-1 会把最右侧的 1 借位借掉,它右边全翻成 1:
n = 011010100
n - 1 = 011010011
n & (n-1)= 011010000 → 208 ✓
每执行一次就少一个 1,所以循环执行直到为 0,次数就是 1 的个数------这正是统计汉明权重的高效写法(比逐位判断快,跳过所有 0)。
六、异或三大性质
a⊕0=aa⊕a=0a⊕b=b⊕a
再配上结合律 (a^b)^c = a^(b^c),就有了整类"去重"题的解法:一堆数里成对出现的全抵消,剩下的就是落单的那个。
只出现一次的数字(其余都出现两次):全部异或一遍即可。
public int singleNumber(int[] nums) {
int ans = 0;
for (int x : nums) ans ^= x;
return ans;
}
只出现一次的数字 III (其余都出现两次,落单的有两个):先全部异或得到 a^b,再用 lowbit 把两个数分到不同组。
public int[] singleNumber(int[] nums) {
int xor = 0;
for (int x : nums) xor ^= x;
int lb = xor & -xor; // 取最右侧的 1,它必然只属于 a、b 之一
int a = 0, b = 0;
for (int x : nums) {
if ((x & lb) == 0) a ^= x; // 按这一位分成两组,各自组内成对抵消
else b ^= x;
}
return new int[]{a, b};
}
七、汉明距离
两个数不同位的个数 = a ^ b 中 1 的个数(异或后为 1 的位正是二者不同的地方)。
public int hammingDistance(int a, int b) {
int x = a ^ b, cnt = 0;
while (x != 0) { // 每轮干掉最右侧的 1
x &= x - 1;
cnt++;
}
return cnt;
}
以 a=1101、b=1011 为例:a^b = 0110,含 2 个 1,汉明距离 = 2 ✓
八、实战速查
| 想做的事 | 一行写法 |
|---|---|
| 取第 x 位 | (n >> x) & 1 |
| 第 x 位置 1 | n |= 1 << x |
| 第 x 位置 0 | n &= ~(1 << x) |
| 第 x 位翻转 | n ^= 1 << x |
| 提取最右的 1 | n & -n |
| 干掉最右的 1 | n & (n-1) |
| 判断 2 的幂 | n > 0 && (n & (n-1)) == 0 |
| 统计 1 的个数 | 循环 n &= n-1 |
| 不用临时变量交换 | a^=b; b^=a; a^=b; |
补第三个坑 :
1 << 31会溢出成负数(Integer.MIN_VALUE),位掩码涉及高位时用1L << x。
配套题号:LC 191(位 1 的个数)、LC 231(2 的幂)、LC 461(汉明距离)、LC 136 / 137 / 260(只出现一次的数字 I / II / III)。