位运算技巧总结
消去x最后一位的1
1 | x&(x-1) |
比如: 十进制数10的二进制为1010,9的二进制数为1001,那么(1010)&(1001)=1000,现在10的二进制中最后一位的1已经被消去
用途:
可以用来检测一个数是不是2的幂次。
如果一个数x是2的幂次,那么x>0且x的二进制中只有一个1,所以用x&(x-1)把1消去,应该返回0,如果返回了非0值,证明不是2的幂次
计算一个整数二进制中1的个数
因为1可以不断的通过x&(x-1)这个操作消去,所以当最后的值变成0的时候,也就求出了二进制中1的个数
如果将整数A转换成整数B,需要改变多少个比特位.
思考将整数A转换为B,如果A和B在第i(0<=i<32)个位上相等,则不需要改变这个BIT位,如果在第i位上不相等,则需要改变这个BIT位。所以问题转化为了A和B有多少个BIT位不相同。联想到位运算有一个异或操作,相同为0,相异为1,所以问题转变成了计算A异或B之后这个数中1的个数
其他常用操作:
异或操作
异或的性质:
1 2 | x^x=0; x^0=x; |
用途:
数组中,只有一个数出现一次,剩下都出现两次,找出出现一次的数
因为剩下的都出现了两次,那么异或值肯定为0,所以把所有的数异或起来得到的值就是那个数,可以做一下nyoj-528
找球号(三)
经典应用:
1. i+(~i)=-1
i取反再与i相加,相当于把所有二进制位设为1,其十进制结果为-1。
2. 计算n+1与n-1
-~n == n + 1,~n为其取反,负号 ’ - ’ 再对其取反并加1。
~-n == n - 1,思路就是找到最低位的第一个1,对其取反并把该位后的所有位也取反,即01001000变为01000111。
3. 取相反数
思路就是取反并加1,也即~n + 1或者(n ^ -1) + 1。
4. if(x == a) x = b; if(x == b) x = a;
利用^运算符的性质,即得x = a ^ b ^ x。
5. n倍数补全
当n为2的幂时,(x + n - 1) & ~(n - 1)会找到第一个大于x的数,且它正好是n的整数倍。
6. 求二进制中1的个数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #include<bits/stdc++.h> using namespace std; int lowbits( int x){ return x&-x; } int main(){ int n; cin>>n; while (n--){ int x; cin>>x; int res=0; while (x){ x-=lowbits(x); res++; } cout<<res<< " " ; } } |
7. 判断二进制中1的奇偶性
1 2 3 4 5 6 7 | x = x ^ (x >> 1); x = x ^ (x >> 2); x = x ^ (x >> 4); x = x ^ (x >> 8); x = x ^ (x >> 16); cout << (x & 1) << endl; // 输出 1 为奇数 |
以下分析摘自Matrix67-位运算,并作稍微修改,
以十进制数1314520为例,其二进制为0001 0100 0000 1110 1101 1000。
第一次异或操作的结果如下:
0001 0100 0000 1110 1101 1000
^ 0000 1010 0000 0111 0110 1100
= 0001 1110 0000 1001 1011 0100
1 2 3 | 0001 0100 0000 1110 1101 1000 ^ 0000 1010 0000 0111 0110 1100 = 0001 1110 0000 1001 1011 0100 |
得到的结果是一个新的二进制数,其中右起第i位上的数表示原数中第i和i+1位上有奇数个1还是偶数个1。比如,最右边那个0表示原数末两位有偶数个1,右起第3位上的1就表示原数的这个位置和前一个位置中有奇数个1。
对这个数进行第二次异或的结果如下:
0001 1110 0000 1001 1011 0100
^ 0000 0111 1000 0010 0110 1101
= 0001 1001 1000 1011 1101 1001
1 2 3 | 0001 1110 0000 1001 1011 0100 ^ 0000 0111 1000 0010 0110 1101 = 0001 1001 1000 1011 1101 1001 |
结果里的每个1表示原数的该位置及其前面三个位置中共有奇数个1,每个0就表示原数对应的四个位置上共偶数个1。
一直做到第五次异或结束后,得到的二进制数的最末位就表示整个32位数里1的奇偶性。
8. 判断奇偶性
1 2 3 4 5 | /* 判断是否是奇数 */ bool is_odd( int n) { return (n & 1 == 1); } |
9. 不用临时变量交换两个数
1 2 3 4 | /* 此方法对 a 和 b 相等的情况不适用 */ a ^= b; b ^= a; // 相当于 b = b ^ ( a ^ b ); a ^= b; |
10. 取绝对值
1 2 3 4 5 | /* 注意:以下的数字 31 是针对 int 大小为 32 而言 */ int abs ( int n) { return (n ^ (n >> 31)) - (n >> 31); } |
其中n >> 31取得n的正负号。
若n为正数,n >> 31的所有位等于0,其值等于0。表达式转化为n ^ 0 - 0,等于n;
若n为负数,n >> 31的所有位等于1,其值等于-1。表达式转化为(n ^ -1) + 1,这很好理解,负数的相反数就是对其补码取反再加1,(n ^ -1) + 1就是在做这样的事。
11. 取两数的较大值
1 2 3 4 5 | /* 注意:以下的数字 31 是针对 int 大小为 32 而言 */ int max( int a, int b) { return (b & ((a - b) >> 31)) | (a & (~(a - b) >> 31)); } |
如果a >= b,(a - b) >> 31为0,否则为-1。
12. 判断符号是否相同
1 2 3 4 5 | /* 若 x,y 都为 0,输出真;若只有一个为 0,不会报错但运行结果是错的,因为 0 没有正负之分 */ bool is_same_sign( int x, int y) { return (x ^ y) >= 0; } |
13. 判断一个数是不是2的幂
1 2 3 4 | bool is_power_of_two( int n) { return (n > 0) ? (n & (n - 1)) == 0 : false ; } |
如果是2的幂,n - 1就是把n的二进制的最低的那个1取反为0,并把后面的0全部取反为1。
14. 取余2的幂次方
1 2 3 4 5 | /* 其中 m 为 2 的幂次方,并对 m 取余 */ int mod( int n, int m) { return n & (m - 1); } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人