位运算技巧总结

1. 判断数字是否为2的幂

技巧 : n & (n-1) == 0n > 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 = 0a ^ 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) & 1n & (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)
相关推荐
耳总是一颗苹果24 分钟前
排序---插入排序
数据结构·算法·排序算法
胡gh25 分钟前
数组开会:splice说它要动刀,map说它只想看看。
javascript·后端·面试
Pure_Eyes26 分钟前
go 常见面试题
开发语言·后端·golang
YLCHUP40 分钟前
【联通分量】题解:P13823 「Diligent-OI R2 C」所谓伊人_连通分量_最短路_01bfs_图论_C++算法竞赛
c语言·数据结构·c++·算法·图论·广度优先·图搜索算法
花火|1 小时前
算法训练营day62 图论⑪ Floyd 算法精讲、A star算法、最短路算法总结篇
算法·图论
GuGu20241 小时前
新手刷题对内存结构与形象理解的冲突困惑
算法
汤永红1 小时前
week4-[二维数组]平面上的点
c++·算法·平面·信睡奥赛
Cisyam2 小时前
使用Bright Data API轻松构建LinkedIn职位数据采集系统
后端
float_六七2 小时前
Spring Boot 3为何强制要求Java 17?
java·spring boot·后端
晴空闲雲2 小时前
数据结构与算法-字符串、数组和广义表(String Array List)
数据结构·算法