从位的角度看数据,发现O(1)的魔法操作
位运算是直接操作二进制位的技巧,可以将某些操作从 O(n) 优化到 O(1) 或 O(log n)。面试中常考:位计数、位掩码、异或技巧。理解位运算的关键是把数字看作二进制串。
n & 1 — 最低位为1是奇数,为0是偶数。比 n % 2 更快。
n & (n - 1) — 这是最经典的位运算技巧!例如 1010 & 1001 = 1000,最低位的1被清除了。
n & (-n) — 利用补码特性。例如 1010 & 0110 = 0010,只保留最低位的1。
# Python 整数无限精度,注意负数右移
-1 >> 1 # → -1 (算术右移,高位补1)
# 获取32位无符号值
n & 0xFFFFFFFF
# 二进制表示
bin(10) # → '0b1010'
format(10, '032b') # → '00000000000000000000000000001010'
# 位计数
bin(10).count('1') # → 2 (但面试不能直接用)
n & (n-1) 是位运算最重要的技巧!它可以:①清除最低位的1(计数1的个数);②判断是否是2的幂(只有1个1);③在位掩码中遍历子集。务必牢记!题目描述:给定一个无符号整数,返回其二进制表达式中1的个数(也称作汉明权重)。
n &= n - 1 清除一个1,直到 n 变为0。执行次数就是1的个数。比逐位检查更高效,因为只遍历1的个数次。
count += n & 1; n >>= 1。
class Solution:
def hammingWeight(self, n: int) -> int:
count = 0
while n:
n &= n - 1 # 清除最低位的1
count += 1
return count
时间复杂度:O(k),k 为 1 的个数(最多32)
空间复杂度:O(1)
n & 0xFFFFFFFF 处理。题目描述:给定整数 n,对于 0 ≤ i ≤ n 的每个 i,计算其二进制中1的个数,返回数组。
dp[i] = dp[i >> 1] + (i & 1)。i 右移一位等价于去掉最低位,dp[i>>1] 已经计算好了去掉最低位后的1的个数,再加上最低位是否为1即可。dp[i] = dp[i & (i-1)] + 1,利用 i & (i-1) 清除最低位的1。
class Solution:
def countBits(self, n: int) -> List[int]:
dp = [0] * (n + 1)
for i in range(1, n + 1):
dp[i] = dp[i >> 1] + (i & 1)
return dp
时间复杂度:O(n)
空间复杂度:O(n)
题目描述:两个整数之间的汉明距离是这两个数对应二进制位不同的位置数目。
n & (n-1) 技巧计数。
class Solution:
def hammingDistance(self, x: int, y: int) -> int:
xor = x ^ y
count = 0
while xor:
xor &= xor - 1
count += 1
return count
时间复杂度:O(k),k 为汉明距离
空间复杂度:O(1)
# 2的幂只有一个1
def is_power_of_two(n):
return n > 0 and (n & (n - 1)) == 0
# 原理: 8 = 1000, 7 = 0111
# 8 & 7 = 0 → 2的幂
# 6 = 0110, 5 = 0101
# 6 & 5 = 0100 ≠ 0 → 不是2的幂
a ^= b
b ^= a
a ^= b
# 原理: a^b^b = a, a^b^a = b
# 面试炫技用,实际代码别这么写(可读性差)
# 枚举集合 {0,1,...,n-1} 的所有子集
n = 3
for mask in range(1 << n):
subset = [i for i in range(n) if mask & (1 << i)]
print(f"mask={mask:03b}, subset={subset}")
# 输出:
# mask=000, subset=[]
# mask=001, subset=[0]
# mask=010, subset=[1]
# mask=011, subset=[0,1]
# mask=100, subset=[2]
# mask=101, subset=[0,2]
# mask=110, subset=[1,2]
# mask=111, subset=[0,1,2]
# LC 136: 数组中只有一个数出现一次,其余出现两次
def single_number(nums):
result = 0
for num in nums:
result ^= num
return result
# 原理: a^a=0, 所有成对的数异或抵消
# 剩下的就是只出现一次的数
n & (n-1):清除最低位的1,用于计数、判断2的幂n & (-n):获取最低位的1,用于树状数组dp[i] = dp[i>>1] + (i&1),利用二进制关系