位运算——数0的个数

来源:互联网 发布:南昌工程学院网络 编辑:程序博客网 时间:2024/04/30 19:17

最简单的方法

移位来运算:N>>=1;然后用N 同1进行“与”运算,来判断末尾是否为1

下面有更快的方法;

快速的方法:判断某一位置是否是1的一个方法,v&=(v-1);

最经典:

      位操作比除、余操作的效率高了很多。但是,即使采用位操作,时间复杂度仍为O(log2v),log2v为二进制数的位数。那么,还能不能再降低一些复杂度呢?如果有办法让算法的复杂度只与"1"的个数有关,复杂度不就能进一步降低了吗?

     同样用10 100 001来举例。如果只考虑和1的个数相关,那么,我们是否能够在每次判断中,仅与1来进行判断呢?

     为了简化这个问题,我们考虑只有一个1的情况。例如:01 000 000。

     如何判断给定的二进制数里面有且仅有一个1呢?可以通过判断这个数是否是2的整数次幂来实现。另外,如果只和这一个"1"进行判断,如何设计操作呢?我们知道的是,如果进行这个操作,结果为0或为1,就可以得到结论。

     如果希望操作后的结果为0,01 000 000可以和00 111 111进行"与"操作。

     这样,要进行的操作就是 01 000 000 &(01 000 000 - 00 000 001)= 01 000 000 &00 111 111 = 0。

因此就有了解法三的代码:

‍    循环中直接计算1的数量(即后面的方法3)
     如何只数'1'的个数?如果一个数字至少包含一个'1'位,那么这个数字减1将从最低位开始依次向高位借位,直到遇到第一个不为'0'的位。依次借位使得经过的位由原来的'0'变为'1',而第一个遇到的那个'1'位则被借位变为'0'。
     36 d = 100100 b
     36-1 d = 100011 b
     如果最低位本来就是'1',那么没有发生借位。
     现在把这2个数字做按位与:n & (n-1)的结果是什么?
     2个数字在原先最低为'1'的位以下(包括这个位)的部分都不同,所以结果是保留了其他的'1'位。
     36 & (36-1) d = 100000 b
     这个结果刚好去掉了最低的一个'1'位
int bit_count(unsigned int n)
{
    int count;
    for(count = 0; n; n &= n - 1)
    {
        count++;
    }
    return count;
}

由于直接跳过'0'位,这个方法比上面的要略微快一些。

2016.8.21更新

复习的时候,经过几个月知识的积累。发现树状数组中的lowbit也可以解决这个问题

如下,因为lowbit就是取一个数的最后的“1”位

while (n) {ans++;n^=lowbit(n);}



1 0
原创粉丝点击