开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情
位运算简介
1:按位或 | :有1为1
2:按位与 & :有0位0
3:按位异或 ^ : 相同为0,不同为1
原码:一个数转成二进制的形式
补码:正数的补码是原码,负数是符合位不变,原码取反加一
补码转原码:正数不变,负数其余各位取反,然后再整个数加1。
4:左移 << :例如: x << k,就是x*2^k
5: 右移 >> : 例如: x >> k,就是x/2^k
常用技巧:
1:去掉最后一位
x >> 1;
2: 在最后一位加一位0
x << 1;
3:在最后一位加一位1
(x << 1) | 1;
4:右数第k位变成1
x | (1 << (k-1));
5:右数第k位变成0
x & ~(1 << (k-1));
6:获得右数第k位
(x >> (k-1)) & 1;
7:截取最后k位
x & ((1 << k) - 1);
8:把右边连续的1变成0
x & (x + 1);
9:把右边第一个的0变成1
x | (x + 1);
10:把右边连续的0变成1
x | (x - 1);
11:把右边第一个的0变成1
x & (x - 1);
12: 截取右边连续的1
(x ^ (x + 1 )) >> 1;
集合枚举
如果集合A的任意一个元素都是集合B的元素,那么集合A称为集合B的子集,集合B称为集合A的超集
例如:n == 5,x = (11001)B
子集:11001,11000,10001,10000,01001,01000,00001,00000
超集:11001,11011,11101,11111
枚举所有状态的非空子集
for(int i = 1;i < 1 << n;i ++)
for(int j = i;j ;j = (j-1)&i)
{
}
枚举所有状态的超集
for(int i = 0;i < 1 << n;i ++)
for(int j = i; ;j = (j+1)|i)
{
if(j == (1 << n) - 1)
break;
}
位运算的应用
位运算一般有三种作用:
- 高效地进行某些运算,代替其它低效的方式。
- 表示集合。
- 题目本来就要求进行位运算。
需要注意的是,用位运算代替其它运算方式(即第一种应用)在很多时候并不能带来太大的优化,反而会使代码变得复杂,使用时需要斟酌。(但像“乘 2 的非负整数次幂”和“除以 2 的非负整数次幂”就最好使用位运算,因为此时使用位运算可以优化复杂度。)