1. 判断数字是否为2的幂
技巧 : n & (n-1) == 0
且 n > 0
原理: 2的幂在二进制中只有一个1位,减1后所有低位都变成1,高位变成0,两者按位与结果为0。
例子:
java
public static boolean isPowerOfTwo(int n) {
return n > 0 && (n & (n - 1)) == 0;
}
// 测试
isPowerOfTwo(8); // 1000 & 0111 = 0000 → true
isPowerOfTwo(6); // 0110 & 0101 = 0100 → false
isPowerOfTwo(16); // 10000 & 01111 = 00000 → true
2. 交换两个变量(无需临时变量)
技巧 : 使用异或运算 a ^= b; b ^= a; a ^= b;
原理 : 异或运算的性质 a ^ a = 0
和 a ^ 0 = a
例子:
java
public static int[] swapValues(int a, int b) {
a ^= b;
b ^= a;
a ^= b;
return new int[]{a, b};
}
// 测试
int[] result = swapValues(5, 9); // 结果: [9, 5]
3. 截取最低位的1
技巧 : n & (-n)
或 n & (~n + 1)
原理: 负数的补码表示使得这个操作能够分离出最低位的1。
例子:
java
public static int getLowestBit(int n) {
return n & (-n);
}
// 测试
getLowestBit(12); // 1100 & (-1100) = 1100 & 0100 = 0100 = 4
getLowestBit(10); // 1010 & (-1010) = 1010 & 0110 = 0010 = 2
getLowestBit(8); // 1000 & (-1000) = 1000 & 1000 = 1000 = 8
4. 清除最低位的1
技巧 : n & (n-1)
原理: n-1会将最低位的1变成0,并将其右边的所有0变成1,按位与后清除最低位的1。
例子:
java
public static int clearLowestBit(int n) {
return n & (n - 1);
}
// 测试
clearLowestBit(12); // 1100 & 1011 = 1000 = 8
clearLowestBit(10); // 1010 & 1001 = 1000 = 8
clearLowestBit(7); // 0111 & 0110 = 0110 = 6
5. 计算二进制中1的个数(汉明重量也叫码重)
技巧 : 反复使用 n & (n-1)
直到n为0
原理: 每次操作都会清除一个1位,操作次数就是1的个数。
例子:
java
public static int countBits(int n) {
int count = 0;
while (n != 0) {
n &= (n - 1); // 清除最低位的1
count++;
}
return count;
}
// JDK内置方法
public static int bitCount(int i) {
// HD, Figure 5-2
i = i - ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
i = (i + (i >>> 4)) & 0x0f0f0f0f;
i = i + (i >>> 8);
i = i + (i >>> 16);
return i & 0x3f;
}
// 测试
countBits(7); // 0111 → 3个1
countBits(15); // 1111 → 4个1
countBits(10); // 1010 → 2个1
6. 判断奇偶性
技巧 : n & 1
原理: 最低位为1表示奇数,为0表示偶数。
例子:
java
public static boolean isOdd(int n) {
return (n & 1) != 0;
}
public static boolean isEven(int n) {
return (n & 1) == 0;
}
// 测试
isOdd(5); // 101 & 001 = 1 → true
isEven(8); // 1000 & 0001 = 0 → true
7. 快速乘除2的幂
技巧 : 左移乘法 n << k
等于 n * 2^k
,右移除法 n >> k
等于 n / 2^k
原理: 二进制左移相当于在末尾添加0,右移相当于去掉末尾位。
例子:
java
public static int multiplyBy8(int n) {
return n << 3; // 左移3位 = 乘以2^3 = 乘以8
}
public static int divideBy4(int n) {
return n >> 2; // 右移2位 = 除以2^2 = 除以4
}
// 测试
multiplyBy8(5); // 5 << 3 = 40
divideBy4(20); // 20 >> 2 = 5
8. 设置第k位为1
技巧 : n | (1 << k)
原理: 1左移k位后与原数按位或,只会影响第k位。
例子:
java
public static int setBit(int n, int k) {
return n | (1 << k);
}
// 测试
setBit(5, 1); // 0101 | 0010 = 0111 = 7
setBit(8, 2); // 1000 | 0100 = 1100 = 12
9. 清除第k位(设为0)
技巧 : n & ~(1 << k)
原理: 1左移k位后取反,与原数按位与,只会清除第k位。
例子:
java
public static int clearBit(int n, int k) {
return n & ~(1 << k);
}
// 测试
clearBit(7, 1); // 0111 & 1101 = 0101 = 5
clearBit(12, 2); // 1100 & 1011 = 1000 = 8
10. 翻转第k位
技巧 : n ^ (1 << k)
原理: 异或运算的性质,与1异或会翻转该位。
例子:
java
public static int toggleBit(int n, int k) {
return n ^ (1 << k);
}
// 测试
toggleBit(5, 1); // 0101 ^ 0010 = 0111 = 7
toggleBit(7, 1); // 0111 ^ 0010 = 0101 = 5
11. 检查第k位是否为1
技巧 : (n >> k) & 1
或 n & (1 << k)
原理: 将第k位移到最低位或者用掩码提取第k位。
例子:
java
public static boolean checkBit(int n, int k) {
return ((n >> k) & 1) != 0;
}
// 或者
public static boolean checkBitAlt(int n, int k) {
return (n & (1 << k)) != 0;
}
// 测试
checkBit(5, 0); // 0101 >> 0 & 1 = 1 → true
checkBit(5, 1); // 0101 >> 1 & 1 = 0 → false
12. 获取两个数的平均值(避免溢出)
技巧 : (a & b) + ((a ^ b) >> 1)
这个很有意义,利用位运算避免 (a + b) / 2
可能的溢出问题。
例子:
java
public static int average(int a, int b) {
return (a & b) + ((a ^ b) >> 1);
}
// 测试
average(10, 14); // (1010 & 1110) + ((1010 ^ 1110) >> 1)
// = 1010 + (0100 >> 1) = 1010 + 0010 = 1100 = 12
13. 求大于等于n的最小2的幂
HashMap源码有用到这个算法 java.util.HashMap#tableSizeFor
技巧: 将n-1的所有位都设为1,然后加1
原理: 通过右移和或运算将最高位1右边的所有位都设为1,最后加1得到下一个2的幂。
例子:
java
public static int nextPowerOfTwo(int n) {
if (n <= 1) return 1;
n--;
n |= n >>> 1; // 使用无符号右移
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return n + 1;
}
// 测试
nextPowerOfTwo(5); // 5 → 8 (2^3)
nextPowerOfTwo(17); // 17 → 32 (2^5)
nextPowerOfTwo(33); // 33 → 64 (2^6)
14. 反转二进制位
技巧: 逐步交换相邻的位、2位组、4位组等
原理: 分治思想,先交换相邻位,再交换相邻的2位组,以此类推。
例子:
java
public static int reverseBytes(int i) {
return ((i >>> 24) ) |
((i >> 8) & 0xFF00) |
((i << 8) & 0xFF0000) |
((i << 24));
}
// 或者使用Java内置方法
public static int reverseBitsBuiltIn(int n) {
return Integer.reverse(n);
}
// 测试: 0b10110000000000000000000000000000
// 结果: 0b00000000000000000000000000001101
15. 计算绝对值(无分支)
技巧 : (n ^ (n >> 31)) - (n >> 31)
(32位整数)
原理: 利用算术右移的符号扩展特性,负数时进行补码转换。
例子:
java
public static int absNoBranch(int n) {
int mask = n >> 31; // 负数时mask全为1,正数时全为0
return (n ^ mask) - mask;
}
// 或者使用Java内置方法
public static int absBuiltIn(int n) {
return Math.abs(n);
}
// 测试
absNoBranch(-5); // mask=0xFFFFFFFF, (-5^mask)-mask = 4-(-1) = 5
absNoBranch(7); // mask=0x00000000, (7^mask)-mask = 7-0 = 7
16. 判断两个数符号是否相同
技巧 : (a ^ b) >= 0
或检查 (a ^ b)
的符号位
原理: 同号数异或结果为正,异号数异或结果为负。
例子:
java
public static boolean sameSignExact(int a, int b) {
return ((a ^ b) >> 31) == 0;
}
// 测试
sameSign(5, 7); // 0101 ^ 0111 = 0010 ≥ 0 → true
sameSign(-3, 4); // 异号 → false
sameSign(-2, -8); // 同为负号 → true
17. 快速模运算(2的幂)
这个大家应该都很熟悉了,因为太常见了
技巧 : n & (m-1)
当m是2的幂时等于 n % m
原理: 2的幂减1后所有低位都是1,按位与相当于保留低位。
例子:
java
public static int fastMod8(int n) {
return n & 7; // 等于 n % 8
}
public static int fastMod16(int n) {
return n & 15; // 等于 n % 16
}
// 通用版本(m必须是2的幂)
public static int fastModPowerOf2(int n, int m) {
return n & (m - 1);
}
// 测试
fastMod8(25); // 25 & 7 = 11001 & 00111 = 00001 = 1
fastMod16(50); // 50 & 15 = 110010 & 001111 = 000010 = 2
18. 计算log₂(n) (整数部分)
技巧: 找到最高位1的位置
原理: 2的幂的对数就是其二进制表示中1的位置。
例子:
java
public static int log2Floor(int n) {
if (n <= 0) return -1;
int log = 0;
while ((n >>>= 1) != 0) {
log++;
}
return log;
}
// 更高效的版本(使用Java内置方法)
public static int log2Fast(int n) {
return 31 - Integer.numberOfLeadingZeros(n);
}
// 测试
log2Floor(8); // 1000 → 3 (2^3 = 8)
log2Floor(15); // 1111 → 3 (2^3 ≤ 15 < 2^4)
log2Floor(32); // 100000 → 5 (2^5 = 32)