位运算技巧

这篇博客探讨了位运算在处理数字时的各种高效方法,包括检查数字是否有重复数字、获取状态码、判断数字是否有全集、进行子集遍历、计算二进制中1的个数以及处理相邻1的情况等。位运算被巧妙地用于集合操作,提高算法的效率。
摘要由CSDN通过智能技术生成

(1)判断数字x的每一位没有重复数字(位运算用作集合)

bool check(int x){
	int status = 0; //标志状态变量,每一位分别表示数字9876543210是否出现过
	while (x){
		int bit = 1 << (x % 10); 
		if (status & bit != 0) return false; //之前已经出现过
		status |= bit; //添加某一位应该是 |= 而不是 &=
		x /= 10;	
	}
	return true;
}

(2)得状态码

int get_status(long long m){  //仅仅针对没有重复数字的!!!
	if (m == 0) return 1; //如果m是0的话,得到的状态应该是01,所以应该单独讨论!!!
	int status = 0;
	while (m){
		int bit = 1 << (m % 10);
		status = status | bit;
		m /= 10;
	}
	return status;
}

(2)判断两个数字的每一位是否出现了重复(位运算用作集合)
比如:36和64都出现了6
36 状态为 0001001000,64 状态为 0001010000
0001001000 & 0001010000 == 1这时就发生了重复

根据(1)得到status 然后 & ,如果是1则发生了重复,否则没重复。

(3)判断当前状态是否出现了0-9全部的数字 (位运算用作集合)

if (status == (1 << 10) - 1) return true;
return false;

(4)A 和 B出现是在n个位置上的组合,A和B个数都是0到n

for (int i = 0; i < (1 << n); ++i){
	...
}

(5)异或(不同为1,相同为0):不同为B,相同为A。
在这里插入图片描述

	假设当前层i>0状态变量为x,上一层为 异或+把最高位置为0
	x = x ^ (x >> 1); 
	x &= (1 << (i-1)) - 1; //某位置置为0,&该位为0,其他都为1 !!! 

(6)判断一个整数的二进制中,有多少个1。
(num-1)&num 就会把num最右边的‘1’给去掉。 因为-1之后100会变成001,&一下最右边的’1’就没了 !!!

int nbit(int num){
	int ans = 0;
	while (num){
		ans++;
		num = (num - 1) & num;
	}
	return ans;
}

(7) 判断int变量a后30位是否存在相邻两个都是1的情况。

int j = 3; //011
for (int i = 0; i < 28; ++i){
	if (a & j == j) return true; //别少了==j,因为需要两位都满足等于1
	j <<= 1;
}
return false;

(8)把最右边那个1左边的数字改变。
^1 实现翻转, ^0不变
…x1000 需要 ^ …10000,怎么凑?
…x1000 -> …x1111 再+1 就是10000,所以

//a ^ (a-1)就是把第一个1右边的都搞成1  !!!
tmp = (a ^ (a - 1)) + 1;
a ^= tmp;

(9)元素对应二进制中最左边‘1’所对应的位置。

int block = 1;
while ((1 << block) <= each) {
    block++;
}
 return block - 1;

(10)小总结:
1:a ^ (a-1)就是把第一个1右边的都搞成1,其他不变。
2:(a-1)&a 就是把a最右边的‘1’给去掉
3:
#define lowbit(x) (x & (-x))是取最右边的1,其他均为0的数。
x -= (x & (-x)) 是把最右边的1去掉,所以等价于x = (x - 1)& x; !!!

3.求进制的值

ans = 0;
for (int i = 0; i < str.size(); ++i){
	ans = ans * jinzhi + str[i];
}

4.汉明权重

方法二:
__builtin_popcount(num);
方法二:
int nbit(int num){
	int ans = 0;
	while (num){
		ans++;
		num = (num - 1) & num;
	}
	return ans;
}
// 求 x 的汉明权重
int popcount(int x) {
    int cnt = 0;
    while (x) {
        cnt += x & 1;
        x >>= 1;
    }
    return cnt;
}
// 求 x 的汉明权重
int popcount(int x) {
    int cnt = 0;
    while (x) {
        cnt++;
        x -= x & -x;
    }
    return cnt;
}

5.子集遍历:

// 遍历 u 的非空子集
for (int s = u; s; s = (s - 1) & u) {
  // s 是 u 的一个非空子集
}

6.常用的二进制函数

int变量中1的个数:
cout << __builtin_popcount(num);long long变量中1的个数:
cout << __builtin_popcountll(num);int变量中位的个数:
cout << 32 - __builtin_clz(num);long long变量中位的个数:
cout << 64 - __builtin_clzll(num);

参考链接https://oi-wiki.org/math/bit/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值