Skip to content

位运算

位运算是一种直接对二进制位进行操作的运算方式,在算法中有着广泛的应用。通过位运算,我们可以实现一些高效的操作,同时节省内存空间。

基本位运算操作

1. 按位与(&)

作用:两个位都为1时,结果才为1

应用

  • 判断奇偶:n & 1 为1表示奇数,为0表示偶数
  • 取最低位:n & -n 可以获取n的最低有效位
  • 判断是否是2的幂:n & (n-1) === 0
js
// 判断奇偶
function isOdd(n) {
  return (n & 1) === 1;
}

// 判断是否是2的幂
function isPowerOfTwo(n) {
  return n > 0 && (n & (n - 1)) === 0;
}

2. 按位或(|)

作用:两个位有一个为1时,结果就为1

应用

  • 设置特定位为1
  • 合并两个集合
js
// 设置特定位为1
function setBit(n, pos) {
  return n | (1 << pos);
}

// 合并两个集合
function mergeSets(set1, set2) {
  return set1 | set2;
}

3. 按位异或(^)

作用:两个位相同时为0,不同时为1

应用

  • 交换两个数:a ^= b; b ^= a; a ^= b;
  • 找出只出现一次的数字
  • 判断两个数是否异号
js
// 交换两个数
function swap(a, b) {
  a ^= b;
  b ^= a;
  a ^= b;
  return [a, b];
}

// 找出只出现一次的数字
function singleNumber(nums) {
  let result = 0;
  for (const num of nums) {
    result ^= num;
  }
  return result;
}

4. 按位非(~)

作用:对每一位取反

应用

  • 求补码
  • 配合其他位运算使用
js
// 求补码
function complement(n) {
  return ~n + 1;
}

5. 左移(<<)

作用:将二进制位向左移动,低位补0

应用

  • 快速计算2的幂:1 << n 等于 2^n
  • 创建掩码
js
// 计算2的n次方
function powerOfTwo(n) {
  return 1 << n;
}

// 创建掩码
function createMask(start, length) {
  return ((1 << length) - 1) << start;
}

6. 右移(>>)

作用:将二进制位向右移动,高位补符号位

应用

  • 快速除以2:n >> 1 等于 n/2
  • 提取特定位
js
// 快速除以2
function divideByTwo(n) {
  return n >> 1;
}

// 提取特定位
function getBit(n, pos) {
  return (n >> pos) & 1;
}

位运算技巧

1. 位掩码(Bitmask)

位掩码是一种使用二进制位来表示状态的技术,常用于状态压缩。

js
// 使用位掩码表示状态
const STATE_A = 1 << 0; // 0001
const STATE_B = 1 << 1; // 0010
const STATE_C = 1 << 2; // 0100
const STATE_D = 1 << 3; // 1000

// 设置状态
let state = 0;
state |= STATE_A; // 设置状态A
state |= STATE_C; // 设置状态C

// 检查状态
if (state & STATE_A) {
  // 状态A已设置
}

// 清除状态
state &= ~STATE_A; // 清除状态A

2. 集合操作

使用位运算可以高效地实现集合的基本操作。

js
class BitSet {
  constructor(size) {
    this.bits = new Array(Math.ceil(size / 32)).fill(0);
  }
  
  // 添加元素
  add(n) {
    const index = Math.floor(n / 32);
    const pos = n % 32;
    this.bits[index] |= (1 << pos);
  }
  
  // 删除元素
  remove(n) {
    const index = Math.floor(n / 32);
    const pos = n % 32;
    this.bits[index] &= ~(1 << pos);
  }
  
  // 检查元素是否存在
  has(n) {
    const index = Math.floor(n / 32);
    const pos = n % 32;
    return (this.bits[index] & (1 << pos)) !== 0;
  }
}

3. 位计数

计算一个数的二进制表示中1的个数。

js
// 方法1:逐位检查
function countBits1(n) {
  let count = 0;
  while (n) {
    count += n & 1;
    n >>= 1;
  }
  return count;
}

// 方法2:利用 n & (n-1) 清除最低位的1
function countBits2(n) {
  let count = 0;
  while (n) {
    n &= n - 1;
    count++;
  }
  return count;
}

4. 位运算优化

使用位运算可以优化一些常见的数学运算。

js
// 快速判断是否是2的幂
function isPowerOfTwo(n) {
  return n > 0 && (n & (n - 1)) === 0;
}

// 快速计算绝对值
function abs(n) {
  const mask = n >> 31;
  return (n + mask) ^ mask;
}

// 快速计算两个数的平均值(防止溢出)
function average(a, b) {
  return (a & b) + ((a ^ b) >> 1);
}

应用场景

1. 状态压缩

在解决某些问题时,可以使用位运算来压缩状态,减少内存使用。

js
// 使用位运算表示棋盘状态
class ChessBoard {
  constructor() {
    this.state = 0;
  }
  
  // 设置棋子
  setPiece(row, col) {
    const pos = row * 8 + col;
    this.state |= (1 << pos);
  }
  
  // 检查是否有棋子
  hasPiece(row, col) {
    const pos = row * 8 + col;
    return (this.state & (1 << pos)) !== 0;
  }
}

2. 子集生成

使用位运算可以高效地生成集合的所有子集。

js
function generateSubsets(nums) {
  const n = nums.length;
  const subsets = [];
  
  for (let mask = 0; mask < (1 << n); mask++) {
    const subset = [];
    for (let i = 0; i < n; i++) {
      if (mask & (1 << i)) {
        subset.push(nums[i]);
      }
    }
    subsets.push(subset);
  }
  
  return subsets;
}

3. 位图算法

使用位运算实现高效的位图算法。

js
class BitMap {
  constructor(size) {
    this.bits = new Array(Math.ceil(size / 32)).fill(0);
  }
  
  // 设置位
  set(n) {
    const index = Math.floor(n / 32);
    const pos = n % 32;
    this.bits[index] |= (1 << pos);
  }
  
  // 清除位
  clear(n) {
    const index = Math.floor(n / 32);
    const pos = n % 32;
    this.bits[index] &= ~(1 << pos);
  }
  
  // 检查位
  test(n) {
    const index = Math.floor(n / 32);
    const pos = n % 32;
    return (this.bits[index] & (1 << pos)) !== 0;
  }
}