对于一个字节的变量,求其二进制表示中“1”的个数,要求算法的执行效率尽可能地高。
分析与解法:
【解法一】
可以举一个八位的二进制例子来进行分析。对于二进制操作,除以一个2,原来的数字将会减少一个0。如果除的过程中有余,那么就表示当前位置有一个1。
以10 100 010 为例:
第一次除以2时,商为1 010 001,余为0。
第二次除以二时,商为 101 000,余为1。
因此可以考虑利用整数数据除法的特点,通过想除和判断余数的值来进行分析。
代码如下:
int Count (int v)
{
int num=0;
while(v)
{
if (v%2==1)
num++;
v=v/2;
}
return num;
}
【解法二】
前面的代码看起来比较复杂。我们知道,向右移位操作同样也可以达到相除的目的。唯一不同之处在于,以为之后如何来判断是否有1存在。对于这个问题,再来看一看一个八位的数字:10 100 001。
在向右以为的过程中,我们会把最后一位直接丢弃。因此需要判断最后以为是否为1,而“与”操作可以达到目的。可以把这个八位的数字与0000 0001 进行“与”操作。如果结果为1,则表示当前八位数的最后一位为1,否则为0.
代码如下:
int Count(int v)
{
int num=0;
while(v)
{
v+=v&0x01;
v>>=1;
}
return num;
}
【解法三】
位操作比除、余操作的效率高了很多。但是,即使采用位操作,时间复杂度认为O(log2 v),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 01)=01 000 000&00 111 111=0。
代码为:
int Count(int v)
{
int num=0;
while(v)
{
v&=(v-1);
num++;
}
return num;
}