本篇博文部分思路及图表来自MoreWindows博客,原文链接:https://blog.csdn.net/MoreWindows/article/details/7354571
本文所涉及操作均以C语言为例,以下操作仅能对整型数据使用。
注意:位运算优先级较低,为保证正确,建议添加括号。
符号 | 描述 | 运算规则 |
---|---|---|
& | 与 | 两位都为1时,结果才为1 |
| | 或 | 两位都为0时,结果才为0 |
^ | 异或 | 两位相同为0,不同为1 |
~ | 按位取反 | 唯一的单目运算符,将0变1,1变0 |
<< | 左移 | 左移若干位,高位丢弃,低位补0 |
>> | 右移 | 右移若干位,对无符号数,高位补0 |
不同编译器对有符号数的右移运算处理方法不一样(在Dev C++中负数也是直接除以2
注意:进行左移时,请保证移动的位数小于变量占用的bit的数量。以int型(32bit,取值范围为 [ − 2 31 , 2 31 − 1 ] [-2^{31} , 2^{31}-1] [−231,231−1])为例,左移31位,即1<<31 = 2^31,部分编译器会令变量wrap out成-2^31,但有些编译器会直接报错,因此保险起见最多左移30位
接下来介绍部分位运算技巧
乘除
右移n位,即原始值除以 2 n 2^n 2n,左移n位,原始值乘以 2 n 2^n 2n。
int a=10, b=10;
a <<= 2;
b >>= 2;
a左移2位,即a的值乘以
2
2
2^2
22,也就是乘以4,即40。
b右移2位,即b的值除以
2
2
2^2
22,也就是除以4,即2。
取符号位
判断变量的符号:
int sign = a >> 31;
(a为int型,有32位,右移31位即可得到符号位,若a<0,则sign为-1,其他则为0)。如果a右移31位后的值赋值给布尔型变量,则a<0时布尔值为1,其他为0。
取任意位
取得变量a的第p位(从右往左数,最右边为第0位),赋值给b。
int b = a >> p & 1;
若a的第p位为0,则b为0,否则为1。
实际上还有一种方法:
int b = a & (1 << p);
但该方法没有上一种好,当a的第p位为0时,b为0。当a的第p位为1时,b并非为1,而是随着p变化(等于 2 p 2^p 2p)。
将任意位置为0或1
a |= 1 << p; //将变量a的第p位(从右往左数)置为1
b &= ~(1 << p); //将变量b的第p位(从右往左数)置为0
模拟开关
由于“~”是按位取反,因此对于值为True(也就是00000001)的bool型变量,取反后变成11111110,还是True。因此要模拟开关,需使用异或操作。
bool s = 0;
s = 1^s; //每执行一次,就会从0变1,或从1变0。
取相反数
int signReverse = ~a+1;
由于计算机使用原码、反码、补码的方式存储数据,int范围内所有数字按位取反后加1都等于其相反数。除了 − 2 31 -2^{31} −231,它按位取反加1后等于自己。
上述两种方法结合,可以写出一种非常高端的取绝对值的函数:
int GetAbs(int a)
{
int t=a>>31;
return (a^t)-t;
}
用t取得a的符号位,任何数与0异或值不变,与1异或值取反,之后若a为负数 再减去-1,即加1。
(该方法同样对-2^31无效)
两数交换
void Swap(int a, int b)
{
a ^= b;
b ^= a;
a ^= b;
}
异或满足交换律,且一个数对另一个数连续异或两次,得到它自己。(即a^b^b==a)
这里放一道比较有意思的题:LeetCode 136
题目链接:https://leetcode.com/problems/single-number/
数组中有n个元素,除了其中的一个元素,其他每个元素会在数组中出现两次。找出这个特殊的元素(该元素仅在数组中出现一次)。
常规思路1:排序后查找相邻的数(时间复杂度为 O ( n ∗ l o g 2 ( n ) ) O(n*log_2(n)) O(n∗log2(n)))
常规思路2:直接扫一遍列表计算每个数出现的次数(时间复杂度为 O ( n ) O(n) O(n)但数组会很大)
位运算的思路:0与任意数字a连续异或两次,值仍为0,若只异或一次,值为a。
int singleNumber(int* a, int n){
int ans = 0;
for (int i=0; i<n; i++)
ans ^= a[i];
return ans;
}
判断是否为2的次方
bool isPowerOfTwo(int n){
return n > 0 && (n & (n - 1)) == 0;
}