吉法师的博客

不知道能否追到喜欢的人呀,今年努力下吧~ 2022.1.4

C++位运算总结

位运算是什么?

程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算就是直接对整数在内存中的二进制位进行操作。

实际运用

1.找出数组中只出现过一次的数

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

题目的关键点是,其他数都出现了两次,这时可以想到异或的妙用。

a^a = 0;
a^0 = a;
a^b^a = a^a^b = 0^b = b;

通过前两条特性,以及最后的异或交换律,我们只需要将数组的每个元素依次异或,最后的结果就是只出现一次的那个数了。

如果是其他元素出现过三次,只有一个数出现过一次,该怎么做呢?

稍微转变下思路,

2. 位1的个数

要如何求出一个整数的二进制下,其中有多少个1呢?

设整数为n,进行n&(n-1)

起初我也不清楚这是什么意思,不如举个例子好了。

假设n=1101 n-1=1100 n&(n-1)=1100

也就是将n这个数,去掉了最末尾的1了。

如果n=10110 n-1=10101 n&(n-1)=10100,也还是一样的意思。

通过不断的循环,做了几次这样的操作,就能知道n有几个1了。

代码:

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int result =0;
        while(n)
        {
            n=n&(n-1);
            result++;
        }
        return result;
    }
};

引申一下,判断一个数是不是2的幂次方,要怎么做呢?

2的幂次方,都只有一个1,比如4(100),8(1000),也是同样的解法了。

3. 将2进制数颠倒

颠倒给定的 32 位无符号整数的二进制位.

要怎么做呢?其实就是再申请一个变量。

uint32_t reverseBits(uint32_t n) {
        int res =0 ;
        for(int i=0;i<32;i++)
        {
            res = res<<1;
            res += n&1;
            n=n>>1;
        }
        return res;
    }

每次将这个变量左移1位,空出最后一位来存放数据,也实现了先入的数往前移动的操作。 比如1«1 = 10;

之后,通过n&1的操作,得到n的最后一位。

比如n=1101 n&1=1。

n=n»1, 1101就变成110了,之后再与1,则会得到0.

循环32次,就能实现颠倒了。

4.

没有4了,位运算其实是一个比较小的知识点,有多余的时间不如去学习一下更常用的知识,比如二分查找,动态规划,数据结构等等,能够理解上面的3道例题,以及下列公式的话,也算是基本了解了。

  • 位移操作 » 和«

对于二进制数来说,左移一位就是在末尾增加了一个0,右移则是抹掉最后一位数。转换成具体的数字来看: 2=0b10 2«1 = 0b100; 2=0b10 2»1 = 0b1; 将结果转成二进制来看,不难得出的是,位移1格就相当于乘以2或者除以2。位移多次,也就是乘以或除以2的次方。

  • 异或操作

老生常谈的A^A=0 A^0=A 以及异或的交换律,知道这样也就足够了。

如果说要比较冷门的知识,那就是通过异或操作,来交换两个变量的值了。 a=a^b; b=a^b; a=a^b;

  • 提取末尾的1

n&(n-1),运算后结果就是去掉最末的1,变成0。

公式表

功能 效果 代码表示
去掉最后一位 101101->10110 x>>1
在最后加一个0 101101->1011010 x<<1
在最后加一个1 101101->1011011 (x<<1)+1
把最后一位变成1 101100->101101 x|1
把最后一位变成0 101101->101100 (x|1)-1
最后一位取反 101101->101100 x^1
把右数第K位变成1 101001->101101,k=3 x|(1<<(k-1))
把右数第K位变成0 101101->101101,k=3 x & ~(1<<(k-1))
右数第k位取反 101001->101101,k=3 x^(1<<(k-1))
取末三位 1101101->101 x&7
取末k位 1101101->1101,k=5 x&(1<<k-1)
取右数第k位 1101101->1,k=4 x >> (k-1)&1
把末k位变成1 101001->101111,k=4 x|(1<<k-1)
末k位取反 101001->100110,k=4 x^(1<<k-1)
把右边连续的1变成0 100101111->100100000 x&(x+1)
把右起第一个0变成1 100101111->100111111 x|(x+1)
把右边连续的0变成1 11011000->11011111 x|(x-1)
取右边连续的1 100101111->1111 (x^(x+1))>>1
去掉右起第一个1的左边 100101000->1000 x&(x^(x-1))

Share