Tizeng's blog Ordinary Gamer

位运算常见套路

2019-02-18
Tizeng

二进制转换

如何将十进制的整数转为二进制?常用的方法是对2不停用竖式取余,直到结果为0,再把得到的一列余数从下到上排列即可。

位运算常用方法

异或(xor)

异或,即两边相同得到0,不同得到1,为了方便记忆,可以理解为没有进位的二进制加法。 注意任何数字和0异或都会得到它自身,即a^0=a

异或还有一个很重要的性质a^a=0,即b^a^a=b。这个性质会被应用在下面的题目中。

左右移位

顾名思义将一个二进制数向左或向右移动若干位,移出边界的数字被删除,另一端多出的位用0补齐。这里有一种特殊情况,当被右移的数字是一个有符号且为负的整数时,最左端会被1补齐。

当我们需要确定一个数中某个位是0还是1时,可以将1向左移动相应的位数,然后求“与”(&)。

n & (n - 1)

这个式子的目的是抹掉n的二进制表示的数的最后一个1(将其变为0)。它的用处非常广,比如可以很方便的找出一个数字的二进制数中有多少个1。

例题

1.数组中只出现一次的数字

题目描述:一个整型数组里除了两个数字之外,其他的数字都出现了偶数次。请找出这两个只出现一次的数字。

这道题考察对异或的应用,首先我们考虑比较简单的情况:如果一个数组中除了一个数字之外,其他数字都出现了偶数次,如何找到这个只出现一次的数字。 我们利用异或的性质,如果将这个数组中的所有数字逐一异或,那么最后我们得到的结果就是这个只出现了一次的数字,因为出现偶数次的数字全都异或成了0。

现在再来看有两个只出现一次的数字的情况,类似的,我们可以利用异或的性质,如果将这个数组分为两组,这两个数字刚好出现在这两个组中,问题不就回到了上面讨论的简单情况了吗?因此我们需要一个将两个数字区分开的标准,这个也可以利用异或来实现,如果将数组中的所有元素都一一异或,得到的结果将会是这两个只出现一次数字的异或结果,而我们只需要取其中任意一位进行判断,就可以区分这两个数。比如这里取异或结果的最右边的1,如果数组中的数相应位置是1则分到一个组,是0则分到另一个组,这样两个出现一次的数必定会分在不同的组中,同时也保证了组中的其他数字都出现了偶数次,因为只要是相同的数字,所有位都必定相同。

以下是实现代码:

void FindNumsAppearOnce(vector<int> data, int* num1, int *num2) {
    if(data.size() < 2)
        return;
    int temp = 0;
    for(const int& n : data)
        temp ^= n;
    *num1 = 0;
    *num2 = 0;
    int b = 1;
    while(1){
        if(b & temp)
            break;
        else
            b = b << 1;
    }
    for(int i = 0; i < data.size(); i++){
        if(data[i] & b){
            *num1 ^= data[i];
        }
        else
            *num2 ^= data[i];
    }
}

2.按位反转一个32bit的整数

uint32_t reverseBits(uint32_t n) {
    uint32_t res = n;
    res = (res >> 16) | (res << 16);
    res = ((res & 0xff00ff00) >> 8) | ((res & 0x00ff00ff) << 8);
    res = ((res & 0xf0f0f0f0) >> 4)| ((res & 0x0f0f0f0f) << 4);
    res = ((res & 0xcccccccc) >> 2) | ((res & 0x33333333) << 2);
    res = ((res & 0xaaaaaaaa) >> 1) | ((res & 0x55555555) << 1);
    return res;
}

或者还有另一种思路更容易理解,从右到左依次取输入整数的二进制位,再加到左移了一位的res中,直到所有位都取到。

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

Comments

Content