《编程之美》——求二进制数中1的个数

问题:
求一个字节(8bit)的无符号整型变量二进制表示中“1”的个数。要求执行效率尽可能高。

分析与解法:

【解法一】
每次除2取余,若为奇数则累加,最终累加结果为“1”的个数。
如,10100010除以2,商为1010001,余数为0;1010001除以2,商为101000,余数为1。时间复杂度O(log2v)

代码:

int count(int v)
{
    int num = 0;
    while(v)
    {
        if(v % 2 == 1)
            num++;
        v /= 2;
    }
    return num;
} 

【解法二】
将数字与00000001进行“与”操作,若结果为1,说明数字最后一位是1,累加,然后对数字进行右移。
位操作比算数运算的效率高,但时间复杂度仍为O(log2v)

代码:

int Count(int v)
{
    int num = 0;
    while(v)
    {
        if(v & Ox01)
            num++;
        v >>= 1;
    }
    return num;
} 

但是如果v为负数,因为每次右移高位补的数字是其符号位,那么最终v会变成0xFFFFFFFF而陷入死循环。

改进的代码为:

int Count(int v)
{
    int num = 0;
    //将无符号的0x00000001进行左移,从而避免了v右移出现死循环的情况
    unsigned int flag = 1;
    while(flag)
    {
        if(v&flag)
            num++;
        flag <<= 1;
    }
    retrun num;
} 

【解法三】
v & (v -1)操作每次能消去数字二进制表示中最后一位1,该操作的次数即为数字中“1”的个数。
时间复杂度为O(m),m为v中1的个数。

代码:

int Count(int v)
{
    while(v)
    {
        v &= (v - 1);
        num++;
    }
} 

【解法四】
因为数字只有8位,可以使用空间换时间。查表法,类似map的思想,即将每个数字中1的个数记录在数组中,v作为数组的下标,每次只要直接查找arr[v]的值,时间复杂度为O(1)

扩展问题:

  1. 如果变量是32位DWORD,使用或改进上述哪个算法?
  2. 求整数A与B二进制表示的汉明距离。

分析与解法:

【问题1】
由于数字长度较大,不能使用解法四,可以使用解法三或改进解法二。

【问题2】
可以先将A与B异或,得到的数值再求其中“1”的个数。

你可能感兴趣的:(《编程之美》——求二进制数中1的个数)