关于位运算在算法中的应用
位运算是一种直接操作二进制位的运算方式,在算法中有着独特的作用。它看似简单,却能帮助我们实现许多高效的算法,特别是在数据处理和优化方面,位运算能让代码更加简洁、执行速度更快。本文将介绍几种常用的位运算及其在经典算法中的应用,通过具体的代码示例展示如何利用位运算高效地解决问题。希望这些例子能让你对位运算在算法中的价值有更直观的理解。
1. 异或运算(XOR)
1.1 找到数组中只出现一次的数字
在一个数组中,其他数字都出现了两次,只有一个数字出现了一次。可以通过异或运算找到这个唯一的数字。
def find_unique(nums):
result = 0
for num in nums:
result ^= num # 将所有数字异或,最终结果为那个只出现一次的数字
return result
# 示例
print(find_unique([2, 3, 2, 4, 3])) # 输出 4
1.2 找到数组中只出现一次的两个数字
在一个数组中,其他数字都出现两次,只有两个数字出现了一次。通过异或和分组找到这两个数字。
def find_two_unique(nums):
xor_all = 0
for num in nums:
xor_all ^= num # 先将所有数字异或,得到两个不同数字的异或结果
diff_bit = xor_all & -xor_all # 找到异或结果中最低位的1
num1, num2 = 0, 0
for num in nums:
if num & diff_bit:
num1 ^= num # 根据最低位的1将数组分为两组分别异或
else:
num2 ^= num
return num1, num2
# 示例
print(find_two_unique([1, 2, 1, 3, 2, 5])) # 输出 (3, 5)
1.3 交换两个数
无需临时变量,直接用异或交换两个数的值。
def swap(a, b):
a ^= b
b ^= a
a ^= b
return a, b
# 示例
print(swap(3, 4)) # 输出 (4, 3)
2. 与运算(AND)
2.1 判断一个数是否是2的幂
2的幂在二进制表示中只有一位是1,n & (n - 1)判断是否为0即可判断是否是2的幂。
def is_power_of_two(n):
return n > 0 and (n & (n - 1)) == 0
# 示例
print(is_power_of_two(8)) # 输出 True
print(is_power_of_two(10)) # 输出 False
2.2 判断一个数的奇偶性
利用n & 1来判断一个数是否为奇数(结果为1)或偶数(结果为0)。
def is_odd(n):
return n & 1 == 1
# 示例
print(is_odd(5)) # 输出 True
print(is_odd(6)) # 输出 False
2.3 统计二进制中1的个数(汉明重量)
将n与n - 1进行与运算,可以消掉最后一个1位,循环直到n为0。
def count_ones(n):
count = 0
while n:
n &= n - 1 # 每次消除最低位的1
count += 1
return count
# 示例
print(count_ones(9)) # 输出 2 (9的二进制是1001)
3. 或运算(OR)
3.1 将指定位置为1
在一些算法中,我们需要将特定位置(bit)设置为1,例如用来标记或启用某些特定功能。
def set_bit(number, position):
return number | (1 << position)
# 示例
print(bin(set_bit(0b1001, 1))) # 输出 0b1011,将第1位设置为1
4. 移位运算(Shift)
4.1 判断一个数是否是4的幂
4的幂的二进制中,1总是在奇数位。通过n & (n - 1) == 0判断它是2的幂,再通过n % 3 == 1判断它是4的幂。
def is_power_of_four(n):
return n > 0 and (n & (n - 1)) == 0 and (n & 0x55555555) != 0
# 示例
print(is_power_of_four(16)) # 输出 True
print(is_power_of_four(8)) # 输出 False
4.2 用位移实现乘除法
左移一位相当于乘2,右移一位相当于除2。
def multiply_by_two(n):
return n << 1
def divide_by_two(n):
return n >> 1
# 示例
print(multiply_by_two(3)) # 输出 6
print(divide_by_two(8)) # 输出 4
5. 取反运算(NOT)
5.1 获取一个数的补码表示
在计算机中,负数常用补码来表示,~n可以获取n的补码形式。
def find_complement(num):
# 用~num得到补码
mask = (1 << num.bit_length()) - 1
return ~num & mask
# 示例
print(find_complement(5)) # 输出 2,5的二进制是101,补码是010
6. 组合位运算应用
6.1 生成集合的所有子集
可以用数字的二进制表示生成集合的子集。对于长度为n的集合,0到2^n - 1的每个整数对应一个子集。
def generate_subsets(nums):
n = len(nums)
subsets = []
for i in range(1 << n): # 2^n次迭代
subset = [nums[j] for j in range(n) if (i & (1 << j)) != 0]
subsets.append(subset)
return subsets
# 示例
print(generate_subsets([1, 2, 3]))
# 输出 [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]