位运算

100 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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;
}

位运算的应用

位运算一般有三种作用:

  1. 高效地进行某些运算,代替其它低效的方式。
  2. 表示集合。
  3. 题目本来就要求进行位运算。

需要注意的是,用位运算代替其它运算方式(即第一种应用)在很多时候并不能带来太大的优化,反而会使代码变得复杂,使用时需要斟酌。(但像“乘 2 的非负整数次幂”和“除以 2 的非负整数次幂”就最好使用位运算,因为此时使用位运算可以优化复杂度。)