转自:https://blog.youkuaiyun.com/qq_30076791/article/details/50571194
& 按位与 如果两个相应的二进制位都为1,则该位的结果值为1,否则为0 AND
| 按位或 两个相应的二进制位中只要有一个为1,该位的结果值为1 OR
^ 按位异或 若参加运算的两个二进制位值相同则为0,否则为1 XOR
~ 取反 ~是一元运算符,用来对一个二进制数按位取反,即将0变1,将1变0
<< 左移 用来将一个数的各二进制位全部左移N位,右补0
>> 右移 将一个数的各二进制位右移N位,移到右端的低位被舍弃,对于无符号数,高位补0
优先级: 位反(~ ) > 算术 > 位左移、位右移 > 关系运算 > 位与 > 位或 > 位异或 > 逻辑运算
异或:
a^b最多等于a+b,最小等于a-b
出现偶数次的数异或值为0
常见用法
1.去掉最后一位
x>>1
2.在最后加一个0
x<<1
3.在最后加一个1
x<<(1+1)
4.把最后一位变成1
x|1
5.把最后一位变成0
x|1-1
6.最后一位取反
x^1
7.把右数第k位变成1
x|(1<<(k-1))
8.把右数第k位变成0
x&~(1<<(k-1))
9.右数第k位取反
x^(1<<(k-1))
10.取末三位
x&7
11.取k位
x&(1<<k-1)
12.取右数第k位
x>>(k-1)&1
13.把末k位变成1
x|(1<<k-1)
14.末k位取反
x^(1<<k-1)
15.把有边连续的1变成0
x&(x+1)
16.把右起第一个0变成1
x|(x+1)
17.把右边连续的0变成1
1|(x-1)
18.取右边连续的1
(x^(x+1))>>1
19.去掉右起第一个1的左边
x&(x^(x-1))
按位与的用途:
如果需要判断一个数a是否为2的次幂,只需要计算出a&(a-1)的值,如果是0的话该数是2的次幂,反之则反之
(1)清零
若想对一个存储单元清零,即使其全部二进制位为0,只要找一个二进制数,其中各个位符合一下条件:
原来的数中为1的位,新数中相应位为0。然后使二者进行&运算,即可达到清零目的。
例:原数为43,即00101011(2),另找一个数,设它为148,即10010100(2),将两者按位与运算:
00101011(2)
&10010100(2)
00000000(2)
(2)取一个数中某些指定位
若有一个整数a(2byte),想要取其中的低字节,只需要将a与8个1按位与即可。
a 00101100 10101100
b 00000000 11111111
c 00000000 10101100
(3)保留指定位:
与一个数进行“按位与”运算,此数在该位取1.
例如:有一数84,即01010100(2),想把其中从左边算起的第3,4,5,7,8位保留下来,运算如下:
01010100(2)
&00111011(2)
00010000(2)
即:a=84,b=59 c=a&b=16
按位异或的用途
1)使特定位翻转
设有数01111010(2),想使其低4位翻转,即1变0,0变1.可以将其与00001111(2)进行“异或”运算,
即:
01111010
^00001111
01110101
运算结果的低4位正好是原数低4位的翻转。可见,要使哪几位翻转就将与其进行∧运算的该几位置为1
即可。
(2)与0相“异或”,保留原值
例如:012^00=012
00001010
^00000000
00001010
因为原数中的1与0进行异或运算得1,0^0得0,故保留原数。
(3) 交换两个值,不用临时变量
例如:a=3,即11(2);b=4,即100(2)。
想将a和b的值互换,可以用以下赋值语句实现:
a=a∧b;
b=b∧a;
a=a∧b;
a=011(2)
(∧)b=100(2)
a=111(2)(a∧b的结果,a已变成7)
(∧)b=100(2)
b=011(2)(b∧a的结果,b已变成3)
(∧)a=111(2)
a=100(2)(a∧b的结果,a已变成4)
等效于以下两步:
① 执行前两个赋值语句:“a=a∧b;”和“b=b∧a;”相当于b=b∧(a∧b)。
② 再执行第三个赋值语句: a=a∧b。由于a的值等于(a∧b),b的值等于(b∧a∧b),因此,相当于a=a∧b∧b∧a∧b,即a的值等于a∧a∧b∧b∧b,等于b。
很神奇吧!
位运算加速技巧
1. 如果乘上一个2的倍数数值,可以改用左移运算(Left Shift) 加速 300%
x = x * 2;
x = x * 64;
//改为:
x = x << 1; // 2 = 21
x = x << 6; // 64 = 26
2. 如果除上一个 2 的倍数数值,可以改用右移运算加速 350%
x = x / 2;
x = x / 64;
//改为:
x = x >> 1;// 2 = 21
x = x >> 6;// 64 = 26
3. 数值转整数加速 10%
x = int(1.232)
//改为:
x = 1.232 >> 0;
4. 交换两个数值(swap),使用 XOR 可以加速20%
var t:int = a;
a = b;
b = t;
//equals:
a = a^b;
b = a^b;
a = a^b;
5. 正负号转换,可以加入 300%
i = -i;
//改为
i = ~i + 1; // NOT 写法
//或
i = (i ^ -1) + 1; // XOR 写法
6. 取余数,如果除数为 2 的倍数,可利用 AND 运算加速 600%
x = 131 % 4;
//equals:
x = 131 & (4 - 1);
7. 利用 AND 运算检查整数是否为 2 的倍数,可以加速 600%
isEven = (i % 2) == 0;
//equals:
isEven = (i & 1) == 0;
8. 加速 Math.abs 600% 的写法1,写法2 又比写法1加速 20%
//写法1
i = x < 0 ? -x : x;
//写法2
i = (x ^ (x >> 31)) - (x >> 31);
//写法3
i=x^(~(x>>31)+1)+(x>>31);
9. 比较两数值相乘之后是否拥有相同的符号,加速 35%
eqSign = a * b > 0;
//equals:
eqSign = a ^ b > 0;
其它位运算技巧
1. RGB 色彩分离
var 24bitColor:uint = 0xff00cc;
var r:uint = 24bitColor >> 16;
var g:uint = 24bitColor >> 8 & 0xFF;
var b:uint = 24bitColor & 0xFF;
2. RGB 色彩合并
var r:uint = 0xff;
var g:uint = 0x00;
var b:uint = 0xcc;
var 24bitColor:uint = r << 16 | g << 8 | b;
ps——尤其是在嵌入式中,位运算尤为重要!
应用:
打印数的二进制
void print2jz(ll x)
{
int c[63];
int k=0;
while(x)
{
c[k]=x%2;
k++;
x=x/2;
}
for(int i=k-1;i>=0;i--)
printf("%d",c[i]);
printf("\n");
}
1.不查表统计1的数量
ll BitCnt(unsigned ll n) {
ll value = 0;
while (n) ++value, n &= n - 1;
return value;
}
2.循环位移
unsigned shift(unsigned now, int n, int N) { //n正表示左移,否则为右移
if (n > 0)
now = (now >> (N - n)) | (now << n);
if (n < 0) {
n = -n;
now = (now << (N - n)) | (now >> n);
}
now &= base;
return now;
}
3.k位取反
a=~(~a^(a<<k))
4.判断n是否是2的整次幂 link
bool fun(ll n){
return (!(n & (n-1))) && n;
}
5.lowbit(x)是x的二进制表达式中最低位的1所对应的值。
比如,6的二进制是110,所以lowbit(6)=2。
ll lowbit(ll x){
return x&(-x);
}
6.取出某个数的某一位(2,4,8,16,32等)
bool get_bit(ll t,int x) {
// 在 t 中,取出第 x 位 --从零开始
return t & (1<<(x));
}
7.改位
#define set_bit(x,ith,bool) ((bool)?((x)|(1<<(ith))):((x)&(~(1<<(ith)))));
//从零开始
// 设置 x 的从第 ith 位起连续 k 位 为bol
int mset(ll x,int ith,int k,int bol)
{
while(k --)x = set_bit(x,ith+k,bol);
return x;
}
8.返回某个数的第x位
int get_01(ll z,int x)
{
return (z>>x)&1;
}
POJ 1324
POJ 3460
POJ 2286
POJ 2513
bitset
bitset 是STL库中的二进制容器,根据C++ reference 的说法,bitset可以看作bool数组,但优化了空间复杂度和时间复杂度,并且可以像整形一样按位与或。
使用方法
申明
bitset的申明要指明长度
1
bitset<length> bi
这样就申明了一个长度为length的名叫bi的bitset
赋值
bitset重载了[]运算符,故可以像bool数组那样赋值
bi[2] = 1;
这样就能将第二位赋值为1
常用函数
b1 = b2 & b3;//按位与
b1 = b2 | b3;//按位或
b1 = b2 ^ b3;//按位异或
b1 = ~b2;//按位补
b1 = b2 << 3;//移位
int one = b1.count();//统计1的个数
优化作用
常常碰到处理的数组只有0和1的变化,此时就可以使用bitset优化。比如求两个集合的交集可以使用按位与运算,求并集可以使用按位或运算
常用的成员函数:
b.any() b中是否存在置为1的二进制位?
b.none() b中不存在置为1的二进制位吗?
b.count() b中置为1的二进制位的个数
b.size() b中二进制位数的个数
b[pos] 访问b中在pos处二进制位
b.test(pos) b中在pos处的二进制位置为1么?
b.set() 把b中所有二进制位都置为1
b.set(pos) 把b中在pos处的二进制位置为1
b.reset( ) 把b中所有二进制位都置为0
b.reset( pos ) 把b中在pos处的二进制位置置为0
b.flip( ) 把b中所有二进制位逐位取反
b.flip( pos ) 把b中在pos处的二进制位取反
b.to_ulong( ) 把b中同样的二进制位返回一个unsigned
线段树位运算操作:https://blog.youkuaiyun.com/weixin_41183791/article/details/84791177