前言:“1在内存中不是孤零零的1,而是前面有很多0的二进制串,具体多少个0由变量类型决定——int有31个0,long long有63个0。1的二进制:00000000,00000000,00000000,00000001”
5个位运算技巧(通俗易懂版)
技巧1:判断奇偶性 ⚖️
公式:x & 1
一句话:看最后一位是0还是1
怎么理解?
二进制就像一排灯泡(0灭1亮):
奇数的最后一位总是1: 1(1), 3(11), 5(101), 7(111) 偶数的最后一位总是0: 2(10), 4(100), 6(110), 8(1000)
代码:
if (x & 1) { cout << "奇数"; // 最后一位是1 } else { cout << "偶数"; // 最后一位是0 }技巧2:获取二进制的某一位 🔍
公式:(x >> i) & 1
一句话:把想要的位移到最右边,然后看是0还是1
怎么理解?
比如想知道数字13(1101)的第2位:
13的二进制: 1 1 0 1 第3 第2 第1 第0位 我们要第2位:把数字右移2位 → 11(01)变成 0011 然后看最右边一位:1
代码:
int getBit(int x, int i) { return (x >> i) & 1; // 返回第i位(0或1) } // 例子:getBit(13, 2) = 1技巧3:修改二进制的某一位 ✏️
设为1:x | (1 << i)
一句话:用一个"只有第i位是1"的数去"或"
设为0:x & ~(1 << i)
一句话:用一个"只有第i位是0"的数去"与"
怎么理解?
设为1(或运算):
x = 9(1001),想把第1位变成1 1 << 1 = 0010(只有第1位是1) 1001 | 0010 = 1011(成功!)
设为0(与运算):
x = 11(1011),想把第1位变成0 1 << 1 = 0010 ~(0010) = 1101(只有第1位是0) 1011 & 1101 = 1001(成功!)
代码:
int setBitToOne(int x, int i) { return x | (1 << i); } int setBitToZero(int x, int i) { return x & ~(1 << i); }技巧4:判断是否为2的幂次方 ⚡
公式:(x & (x-1)) == 0
一句话:2的幂就像"1000..."(只有一个1),减1变成"0111...",一与就没了
怎么理解?
2的幂的二进制:
2^0 = 1 = 0001 2^1 = 2 = 0010 2^2 = 4 = 0100 2^3 = 8 = 1000 特点:只有一个1!
减1后:
8(1000) - 1 = 7(0111) 1000 & 0111 = 0000 ✓ 不是2的幂: 6(0110) - 1 = 5(0101) 0110 & 0101 = 0100 ≠ 0 ✗
代码
bool isPowerOfTwo(int x) { return x > 0 && (x & (x-1)) == 0; }技巧5:获取最低位的1(lowbit)🎯
公式:x & -x
一句话:找到二进制中最右边的那个1,其他全变0
怎么理解?
x = 12(二进制1100) 最右边的1在第2位(从0开始数) -x = -12(补码表示:0100,其实是...11110100) 1100 & ...11110100 = 0100(4) 结果:0100,就是最右边那个1!
有什么用?
树状数组的核心操作
快速找"最右边"的1
统计1的个数时可以用
代码:
int lowbit(int x) { return x & -x; } // lowbit(12) = 4 // lowbit(10) = 2(10=1010,最低位1是0010=2)🎓 五个技巧总结
| 技巧 | 公式 | 作用 | 记忆方法 |
|---|---|---|---|
| 判断奇偶 | x & 1 | 看最后一位 | "末位1=奇数" |
| 获取某位 | (x>>i) & 1 | 看第i位 | "右移再看末位" |
| 修改某位 | x|(1<<i)或x&~(1<<i) | 改0或1 | "造面具戴面具" |
| 2的幂判断 | (x&(x-1))==0 | 判断是否只有1个1 | "只有一个1的消掉就没了" |
| 最低位1 | x & -x | 找最右边的1 | "负数取反加1,正好对齐" |