关于位运算在算法中的应用

156 阅读4分钟

关于位运算在算法中的应用

位运算是一种直接操作二进制位的运算方式,在算法中有着独特的作用。它看似简单,却能帮助我们实现许多高效的算法,特别是在数据处理和优化方面,位运算能让代码更加简洁、执行速度更快。本文将介绍几种常用的位运算及其在经典算法中的应用,通过具体的代码示例展示如何利用位运算高效地解决问题。希望这些例子能让你对位运算在算法中的价值有更直观的理解。

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的个数(汉明重量)

nn - 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的集合,02^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]]