位运算符:AND、OR、XOR、NOT 与移位
通过真值表和 Python 示例复习六种位运算符,并理解左移和右移与乘以二、除以二之间的关系。
位运算符:AND、OR、XOR、NOT 与移位 是 CoddyKit 上的免费 Coding Interview Prep 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Coding Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Coding Interview Prep 课程共包含 4 节课。
位运算为何重要
位运算让您可以直接操作整数的二进制表示。许多看似复杂的问题,只要使用正确的位运算技巧就会变得非常简单:例如在 O(n) 时间和 O(1) 空间内寻找缺失的数字、不使用临时变量交换变量,或紧凑地编码子集。面试官使用这些问题来测试您对底层原理的理解和创造性思维。
Python 整数支持任意精度——只要内存允许,它们就可以很大——但位运算在硬件层面始终遵循标准的补码语义。六种运算符都会逐位作用于整数的二进制表示。
# All six bitwise operators in Python
a, b = 0b1010, 0b1100 # 10 and 12 in decimal
print(f'a = {bin(a)} = {a}')
print(f'b = {bin(b)} = {b}')
print(f'a & b (AND) = {bin(a & b)} = {a & b}') # 1000 = 8
print(f'a | b (OR) = {bin(a | b)} = {a | b}') # 1110 = 14
print(f'a ^ b (XOR) = {bin(a ^ b)} = {a ^ b}') # 0110 = 6
print(f'~a (NOT) = {~a}') # -11 (two's complement)
print(f'a << 1 (LSH) = {bin(a << 1)} = {a << 1}') # 10100 = 20
print(f'a >> 1 (RSH) = {bin(a >> 1)} = {a >> 1}') # 101 = 5AND 运算符:位掩码
AND 运算符 (&)仅在两个输入位都为 1 时输出 1。它的主要用途是掩码操作:选出数字中的特定位,同时将其他所有位清零。要检查数字 n 的第 k 位是否为 1,请计算 n & (1 << k);如果结果非零,则第 k 位为 1。
AND 还可用于清除最低位的 1:n & (n - 1) 会移除最右侧的 1 位。这可用于高效统计置 1 位的数量,以及检查一个数字是否为 2 的幂(2 的幂恰好只有一个置 1 位,因此 n & (n-1) == 0)。
n = 0b10110100 # 180
# Check if bit 5 is set (0-indexed from right)
bit_5 = (n >> 5) & 1
print(f'Bit 5 of {n}: {bit_5}') # 1
# Clear lowest set bit
print(f'n = {bin(n)}')
print(f'n & (n-1) = {bin(n & (n-1))}') # 10110000, removed the '100'
# Check power of two
for x in [16, 15, 8, 6, 1, 0]:
is_pow2 = x > 0 and (x & (x - 1)) == 0
print(f'{x}: power of 2 = {is_pow2}')OR 运算符:设置位
OR 运算符 (|)只要至少一个输入位为 1,就会输出 1。它的主要用途是将特定位设置为 1,同时不影响其他位。要将数字 n 的第 k 位设置为 1,请使用 n | (1 << k)。移到第 k 位的 1 会打开该位;由于任何数与 0 进行 OR 运算后仍保持不变,其他所有位都会保持不变。
OR 还可用于组合标志:如果您将功能标志表示为独立的位,就可以使用 OR 启用多个标志。例如,READ | WRITE | EXECUTE 会将三个权限位组合成一个整数。
# Set bit k in n
def set_bit(n, k):
return n | (1 << k)
n = 0b1000 # 8
print(f'Original: {bin(n)}')
print(f'Set bit 1: {bin(set_bit(n, 1))}') # 1010
print(f'Set bit 0: {bin(set_bit(n, 0))}') # 1001
# Flag combination example
READ = 0b001 # 1
WRITE = 0b010 # 2
EXECUTE = 0b100 # 4
perms = READ | EXECUTE
print(f'READ|EXECUTE permissions: {bin(perms)} = {perms}')
print(f'Has READ: {bool(perms & READ)}')
print(f'Has WRITE: {bool(perms & WRITE)}')
print(f'Has EXECUTE: {bool(perms & EXECUTE)}')XOR 运算符:翻转与差异
XOR 运算符 (^)会在输入位不同时输出 1。XOR 具有三个强大的代数性质:a ^ a = 0(相同输入会相互抵消)、a ^ 0 = a(0 是单位元),并且 XOR 同时满足交换律和结合律。这些性质使 XOR 成为寻找唯一元素的首选工具。
XOR 还可用于翻转特定位:n ^ (1 << k) 会翻转第 k 位,同时保持其他位不变。如果第 k 位原来是 0,它会变为 1;如果原来是 1,它会变为 0。
# XOR properties
print(5 ^ 5) # 0 — same values cancel
print(5 ^ 0) # 5 — zero is identity
print(5 ^ 3 ^ 3) # 5 — 3 cancels itself
# Toggle bit k
def toggle_bit(n, k):
return n ^ (1 << k)
n = 0b1010
print(f'Toggle bit 3: {bin(toggle_bit(n, 3))}') # 0010 (was 1)
print(f'Toggle bit 0: {bin(toggle_bit(n, 0))}') # 1011 (was 0)
# XOR swap without temp variable
a, b = 7, 13
a = a ^ b
b = a ^ b # b now gets original a
a = a ^ b # a now gets original b
print(f'After XOR swap: a={a}, b={b}') # a=13, b=7NOT 运算符与补码
NOT 运算符 (~)会翻转所有位。在 Python 中,由于采用补码表示法,~n 等于 -(n+1)。这会让许多人感到意外:~5 = -6,而不是直觉上可能期待的 0b11111010。Python 整数具有无限精度,因此对正数翻转所有位会在补码表示下得到负数。
在实践中,您很少会在 Python 中单独使用 ~ 进行位运算。相反,您可以将它与 AND 结合使用来清除特定位,或计算 ~n & mask,其中 mask 将位宽限制为指定的位数(例如,对 32 位使用 & 0xFFFFFFFF)。
# NOT in Python: ~n = -(n+1)
for n in [0, 1, 5, 127]:
print(f'~{n} = {~n}') # all give -(n+1)
# Clear bit k using NOT
def clear_bit(n, k):
return n & ~(1 << k)
n = 0b1111
print(f'Clear bit 2: {bin(clear_bit(n, 2))}') # 1011
print(f'Clear bit 0: {bin(clear_bit(n, 0))}') # 1110
# Limiting to 32-bit with mask
def bitwise_not_32(n):
return ~n & 0xFFFFFFFF
print(f'32-bit NOT of 5: {bin(bitwise_not_32(5))}') # 32 zeros then ones左移:乘以 2 的幂
左移运算符 (<<)会将所有位向左移动 k 个位置,并用 0 填充右侧空出的位。这相当于乘以2^k。左移 1 位会使数值翻倍;左移 k 位会使数值乘以 2^k。
在面试题中,左移最常用于创建位掩码:1 << k 会创建一个只有第 k 位为 1 的数字。这是所有位运算操作的基础——设置、清除、翻转和检查单个位,都是从 1 << k 开始的。
# Left shift = multiply by 2^k
n = 1
for k in range(8):
print(f'1 << {k} = {1 << k}') # 1,2,4,8,16,32,64,128
# Practical use: creating bitmasks
def bit_mask(k):
return 1 << k
print(f'\nBitmask for bit 0: {bin(bit_mask(0))}') # 1
print(f'Bitmask for bit 3: {bin(bit_mask(3))}') # 1000
print(f'Bitmask for bit 7: {bin(bit_mask(7))}') # 10000000
# Fast exponentiation: 2^10 = 1024
print(f'2^10 = {1 << 10}') # 1024右移:除以 2 的幂
右移运算符 (>>)会将所有位向右移动 k 个位置,并丢弃最右侧的 k 位。这相当于整除 2^k。Python 的右移始终是算术右移:最左侧的位会用符号位填充(正数为 0,负数为 1)。
一个常见的面试技巧是:要提取数字 n 的第 k 位,请使用 (n >> k) & 1。这会将第 k 位移到位置 0,并通过掩码去除其他所有位。这是检查任意特定位最简洁的方法,无需计算并比较完整的掩码。
# Right shift = integer division by 2^k
n = 64
for k in range(7):
print(f'{n} >> {k} = {n >> k}') # 64,32,16,8,4,2,1
# Extract bit k from n
def get_bit(n, k):
return (n >> k) & 1
n = 0b10110101 # 181
print(f'\nBits of {n} ({bin(n)}):')
for k in range(8):
print(f' Bit {k}: {get_bit(n, k)}')
# Negative number right shift (arithmetic)
print(f'-8 >> 1 = {-8 >> 1}') # -4 (fills with sign bit 1)实用位运算技巧速查表
下面汇总了您在面试中会遇到的最常见位运算惯用写法。请记住这些模式——它们会在数十道题中反复出现:
n & 1— 检查 n 是否为奇数n & (n-1)— 清除最低位的 1n & -n— 提取最低位的 1n | (1 << k)— 设置第 k 位n & ~(1 << k)— 清除第 k 位n ^ (1 << k)— 翻转第 k 位(n >> k) & 1— 检查第 k 位
# Bit trick cheatsheet — all at once
n = 0b10110100 # 180
print(f'n = {bin(n)} = {n}')
print(f'n & 1 (odd check) = {n & 1}') # 0: even
print(f'n & (n-1) (clear lowest bit) = {bin(n & (n-1))}')
print(f'n & -n (isolate lowest bit) = {bin(n & -n)}')
print(f'n | (1<<1) (set bit 1) = {bin(n | (1<<1))}')
print(f'n & ~(1<<2) (clear bit 2) = {bin(n & ~(1<<2))}')
print(f'n ^ (1<<5) (toggle bit 5) = {bin(n ^ (1<<5))}')
print(f'(n>>4) & 1 (check bit 4) = {(n>>4) & 1}')统计置 1 位(位计数)
统计整数中 1 位的数量称为人口计数(popcount)。朴素方法会遍历所有位。Brian Kernighan 技巧更快:反复使用 n &= n - 1 清除最低位的 1,并统计循环次数,直到 n 变为 0。每次循环都会恰好移除一个 1 位,因此循环次数正好等于 1 位的数量。
Python 3.10+ 提供了 int.bit_count(),可以直接返回计数结果。在较早的版本中,Kernighan 技巧是标准的手动实现方法。这项技术还可以解决 LeetCode 上的“汉明重量”问题。
# Method 1: naive O(log n)
def count_bits_naive(n):
count = 0
while n:
count += n & 1
n >>= 1
return count
# Method 2: Brian Kernighan O(k) where k = number of set bits
def count_bits_fast(n):
count = 0
while n:
n &= n - 1 # clear lowest set bit
count += 1
return count
# Method 3: Python built-in (3.10+)
# n.bit_count()
for x in [0, 1, 7, 255, 180, 1024]:
naive = count_bits_naive(x)
fast = count_bits_fast(x)
print(f'{x:4d} ({bin(x):10s}): naive={naive}, fast={fast}')Python 中的位运算:重要注意事项
与 C/Java 不同,Python 整数可以任意大——不存在 32 位或 64 位溢出。这意味着在解决要求 32 位行为的问题时,您必须手动将结果掩码到固定宽度:使用 & 0xFFFFFFFF 只保留低 32 位。
Python 中的 NOT 运算符 ~n 返回 -(n+1),而不是您可能根据 C 所期待的按位翻转结果。对于 32 位问题,请使用 ~n & 0xFFFFFFFF,或计算 0xFFFFFFFF ^ n,以得到预期的 32 位补码。许多习惯 C 风格位运算的候选人都会被这些差异绊倒。
# Python vs C gotchas
# In C: unsigned 32-bit NOT of 5 = 4294967290
# In Python: ~5 = -6
print(f'Python ~5 = {~5}') # -6
print(f'32-bit ~5 = {~5 & 0xFFFFFFFF}') # 4294967290
# No integer overflow in Python
big = 1 << 100 # 2^100: huge number, no overflow
print(f'2^100 = {big}') # works fine
# Right shift on negatives: arithmetic (sign-extending)
print(f'-1 >> 3 = {-1 >> 3}') # -1 (all ones shifted in)
# Safe 32-bit mask for problems expecting C/Java semantics
MASK32 = 0xFFFFFFFF
result = (5 + 0xFFFFFFFE) & MASK32 # simulates 32-bit overflow
print(f'5 + (-2) in 32-bit = {result}') # 3移位运算符与乘法
左移和右移提供了通过 2 的幂进行乘除的极快方法。在硬件上,位移是单条指令即可完成的操作,而乘法和除法需要多个周期。在 Python 中,整数乘法本身已经很高效,但理解这种关系有助于您更清晰地观察位模式。
一个实用恒等式是:要检查 n 是否为 2^k 的倍数,请使用 (n & (2^k - 1)) == 0。掩码 2^k - 1 的低 k 位全为 1;与它进行 AND 运算会得到除以 2^k 后的余数。这与 n % (2^k) 等价,但在基于 C 的语言中速度更快。
# Shift vs arithmetic equivalence
for k in range(1, 5):
n = 48
print(f'{n} * 2^{k} = {n * (2**k)} = {n << k} (left shift)')
print(f'{n} // 2^{k} = {n // (2**k)} = {n >> k} (right shift)')
print()
# Check divisibility by power of 2
def divisible_by_power_of_2(n, k):
mask = (1 << k) - 1 # 2^k - 1: lower k bits all 1
return (n & mask) == 0
for n in [16, 24, 32, 15, 100]:
print(f'{n} divisible by 4? {divisible_by_power_of_2(n, 2)}')快速检查
测试您对本课数据结构与算法——编码面试准备相关概念的理解。
课程回顾
本课中您学到了:AND 对位进行掩码,OR 设置位,XOR 翻转位并检测差异,NOT 进行翻转(在 Python 中得到 -(n+1)),移位则通过 2 的幂进行乘除;n & (n-1) 会清除最低位的 1,是检查 2 的幂和统计位数的基础;以及Python 没有固定位宽溢出,因此 32 位问题需要使用 & 0xFFFFFFFF 进行显式掩码。接下来我们将探索 XOR 的自逆性质,用它解决单个数字问题系列。
常见问题解答
「位运算符:AND、OR、XOR、NOT 与移位」课时是免费的吗?
是的 — 「位运算符:AND、OR、XOR、NOT 与移位」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Coding Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 Coding Interview Prep 课程共包含 4 节课。
「位运算符:AND、OR、XOR、NOT 与移位」这节课中我会学到什么?
通过真值表和 Python 示例复习六种位运算符,并理解左移和右移与乘以二、除以二之间的关系。 你通过在浏览器中直接运行的动手代码来练习 Coding Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Coding Interview Prep 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Coding Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「位运算符:AND、OR、XOR、NOT 与移位」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Coding Interview Prep 课中编写并运行代码吗?
能。每节 Coding Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 位运算符:AND、OR、XOR、NOT 与移位
- Single Number 与 XOR 性质
- 位掩码:设置、清除、翻转与检查
- 位计数、缺失数字与位反转