最简单的方法
移位来运算: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‘位,这个方法比上面的要略微快一些。