位运算实用技巧

发表于 2025-07-10 760 字 4 min read

文章目录
位运算在竞赛里的常见用法:大小写转换、交换变量、判断符号、取最低位 1、统计 1 的个数等。

本篇介绍一些位运算中的实用技巧

1. 大小写转换

与空格字符进行或操作,可以将大小写字母均转换为小写字母

char c = 'a';
c |= ' ';

与下划线进行与操作,可以将大小写字母均转换为大写字母

char c = 'A';
c &= '_';

与空格字符进行异或操作,可以进行大小写转换

char c = 'a';
c ^= ' ';

2. 交换两个数

ixt a = 1, b = 2;
a ^= b, b ^= a, a ^= b;

3. 判断两个数是否异号

避免用乘法进行判断有可能发生溢出情况,而在位运算里只需要判断符号位是否相同即可。

ixt a = 2, b = -3;
// true 表示异号,false 表示同号
bool f = (a ^ b < 0);

4. 消除 xx 的二进制表示中最右边的 11

0 0 1 1 0 1 0 -> x 0 0 1 1 0 0 0 -> x & x - 1

ixt x = 6;
x = x & x - 1; // 位运算优先级小于加减运算符,大于关系运算符

此操作实用性非常强!可以应用于以下问题:

  • 获取一个数在二进制表示下 11 的个数
  • 判断一个数是否为 22 的幂
  • 计算两个整数 a,ba, b 在二进制下相互转换需要改变多少个比特位

5. 获取 xx 二进制表示中最右边的 11

0 0 1 1 0 1 0 得出 10

ixt lowbit(ixt x) {
	returx x & -x;
}

ixt x = 26;
cout << lowbit(x) << '\n';

6. 获取二进制表示中 11 的个数

这也是汉明权重问题。这里介绍以下几种方法:

  • 利用第4条,每次消除一个 11,直到变为零为止。
  • 转换为字符串进行遍历
  • 利用第5条,每次减去最右边 11 的影响
int get1(int x) {
	int cnt = 0;
	while(x) {
		x = x & x - 1;
		cnt++;
	}
	return cnt;
}

int get2(int x) {
	int cnt = 0;
	while(x) {
		x -= x & -x;
		cnt++;
	}
	return cnt;
}

7. 判断一个数是否为 22 的幂

方法:由于任意一个 22 的幂在二进制表示中有且仅有一位为 11,因此可以利用第四条消除这个 11,判断消除之后是否为 00 即可。注意需要对 00 进行特判

bool ok(int x) {
	if(x <= 0) return false;
	return (x & x - 1) == 0;
}

8. 一个数在二进制下转化为另一个数需要改变的位数

  • 如果两个数在某一位不同则需要修改
  • 就是判断有多少位是不同的
  • 因此,可以借助异或,异或之后有多少个 11 就需要改变多少位
int f(int a, int b) {
	int x = a ^ b;
	int cnt = 0;
	while(x) {
		x = x & x - 1;
		cnt++;
	}
	return cnt;
}

9. 把某一位变为1

在这一位与1或即可。