30 秒速览

  • 核心操作&(与)、|(或)、^(异或)、~(非)、<<>>
  • 常见技巧n & (n-1) 消除最低 1、n & (-n) 获取最低 1
  • 异或性质a ^ a = 0a ^ 0 = a、满足交换律结合律
  • 典型应用:找唯一数、位掩码、状态压缩 DP

一、直觉建立

位运算是什么?

把数字看成二进制,直接操作每一位。

5 = 0101
3 = 0011
-------
5 & 3 = 0001 (1)
5 | 3 = 0111 (7)
5 ^ 3 = 0110 (6)

开关比喻

把每一位想象成一个开关:

  • & 1:检查开关状态
  • | 1:打开开关
  • & 0:关闭开关
  • ^ 1:切换开关

二、基本操作

位运算符

a & b   # 与:两位都为 1 才为 1
a | b   # 或:有一位为 1 就为 1
a ^ b   # 异或:两位不同为 1
~a      # 非:取反
a << n  # 左移:相当于 × 2^n
a >> n  # 右移:相当于 ÷ 2^n

常用技巧

# 判断奇偶
n & 1  # 1 是奇数,0 是偶数

# 交换两个数
a ^= b
b ^= a
a ^= b

# 消除最低位的 1
n & (n - 1)

# 获取最低位的 1
n & (-n)

# 判断是否是 2 的幂
n > 0 and (n & (n - 1)) == 0

# 取第 k 位
(n >> k) & 1

# 设置第 k 位为 1
n | (1 << k)

# 设置第 k 位为 0
n & ~(1 << k)

# 切换第 k 位
n ^ (1 << k)

三、典型例题

例题 1:LC 136. 只出现一次的数字

题目:数组中除了一个元素只出现一次,其他都出现两次。找出那个元素。

异或法

利用异或性质:

  • a ^ a = 0
  • a ^ 0 = a
  • 异或满足交换律、结合律
def singleNumber(nums: list[int]) -> int:
    result = 0
    for num in nums:
        result ^= num
    return result

复杂度:时间 O(n),空间 O(1)


例题 2:LC 191. 位 1 的个数

题目:统计整数的二进制表示中有多少个 1。

方法一:n & (n-1)

每次消除最低位的 1。

def hammingWeight(n: int) -> int:
    count = 0
    while n:
        n &= (n - 1)  # 消除最低位的 1
        count += 1
    return count

方法二:逐位检查

def hammingWeight(n: int) -> int:
    count = 0
    for _ in range(32):
        count += n & 1
        n >>= 1
    return count

复杂度:时间 O(1)(固定 32 位),空间 O(1)


例题 3:LC 338. 比特位计数

题目:对 0 到 n 的每个数,计算其二进制表示中 1 的个数。

DP 解法

dp[i] = i 的二进制中 1 的个数

def countBits(n: int) -> list[int]:
    dp = [0] * (n + 1)

    for i in range(1, n + 1):
        # i 比 i >> 1 多了最低位
        # 如果 i 是奇数,最低位是 1,否则是 0
        dp[i] = dp[i >> 1] + (i & 1)

    return dp

或者用 i & (i-1)

def countBits(n: int) -> list[int]:
    dp = [0] * (n + 1)

    for i in range(1, n + 1):
        # i 比 i & (i-1) 多一个 1
        dp[i] = dp[i & (i - 1)] + 1

    return dp

复杂度:时间 O(n),空间 O(n)


例题 4:LC 461. 汉明距离

题目:两个整数之间的汉明距离是二进制表示中不同位的个数。

异或后数 1

def hammingDistance(x: int, y: int) -> int:
    xor = x ^ y
    count = 0
    while xor:
        xor &= (xor - 1)
        count += 1
    return count

复杂度:时间 O(1),空间 O(1)


例题 5:LC 268. 丢失的数字

题目:[0, n] 中缺失了一个数字,找出它。

异或法

def missingNumber(nums: list[int]) -> int:
    result = 0
    n = len(nums)

    # 异或所有索引和数值
    for i, num in enumerate(nums):
        result ^= i ^ num

    # 最后异或 n
    return result ^ n

复杂度:时间 O(n),空间 O(1)


例题 6:LC 231. 2 的幂

题目:判断一个数是否是 2 的幂。

位运算

2 的幂只有一个 1。

def isPowerOfTwo(n: int) -> bool:
    return n > 0 and (n & (n - 1)) == 0

例题 7:LC 190. 颠倒二进制位

题目:颠倒给定的 32 位无符号整数的二进制位。

逐位颠倒

def reverseBits(n: int) -> int:
    result = 0
    for _ in range(32):
        result = (result << 1) | (n & 1)
        n >>= 1
    return result

例题 8:LC 421. 数组中两个数的最大异或值

题目:找数组中两个数的最大异或值。

位运算 + 贪心

从最高位开始,贪心地尝试构造最大异或值。

def findMaximumXOR(nums: list[int]) -> int:
    result = 0
    mask = 0

    for i in range(30, -1, -1):
        mask |= (1 << i)

        # 当前所有数的前缀
        prefixes = set(num & mask for num in nums)

        # 尝试把结果的这一位置 1
        candidate = result | (1 << i)

        # 检查是否存在两个前缀异或等于 candidate
        for p in prefixes:
            if p ^ candidate in prefixes:
                result = candidate
                break

    return result

复杂度:时间 O(31 × n),空间 O(n)


四、状态压缩

思想

用整数的二进制位表示状态集合。

例如:有 5 个任务,每个任务完成/未完成,可以用 5 位二进制表示。

例题:LC 864. 获取所有钥匙的最短路径

# 状态压缩 BFS
# state 的第 k 位表示是否有第 k 把钥匙

五、位运算技巧总结

技巧代码用途
判断奇偶n & 11=奇,0=偶
消除最低 1n & (n-1)数 1 的个数
获取最低 1n & (-n)Fenwick 树
判断 2 的幂n & (n-1) == 0只有一个 1
取第 k 位(n >> k) & 1检查位
设置第 k 位n | (1 << k)打开位
清除第 k 位n & ~(1 << k)关闭位
切换第 k 位n ^ (1 << k)翻转位
交换两数a^=b; b^=a; a^=b无临时变量

六、常见错误与陷阱

错误 1:Python 负数右移

# Python 中负数右移是算术右移(符号位扩展)
-1 >> 1  # 还是 -1

# 解决:用位与限制位数
(-1 >> 1) & 0xFFFFFFFF

错误 2:运算符优先级

# 错误
if n & 1 == 1:  # == 优先级高于 &

# 正确
if (n & 1) == 1:

错误 3:忘记括号

# 错误
n & n - 1  # 相当于 n & (n-1),但容易误解

# 正确
n & (n - 1)

错误 4:溢出

# Python 整数无溢出,但其他语言要注意
# 左移可能溢出

七、复盘清单

触发信号

  • 唯一/出现次数问题
  • 二进制操作
  • 状态压缩
  • 权限/标志位

技巧选择

  • 找唯一数 → 异或
  • 数 1 的个数 → n & (n-1)
  • 判断 2 的幂 → n & (n-1) == 0
  • 位操作 → 掩码

实现检查

  • 运算符优先级
  • 负数处理
  • 边界条件(0、-1)

八、题目清单

基础题

题号题目难度考点
136只出现一次的数字Easy异或
191位 1 的个数Easyn&(n-1)
2312 的幂Easyn&(n-1)
268丢失的数字Easy异或
461汉明距离Easy异或

进阶题

题号题目难度考点
338比特位计数MediumDP
190颠倒二进制位Easy位操作
421最大异或值Medium贪心
371两整数之和Medium位运算加法
78子集Medium位枚举