整数的二进制表示中1的个数 【微软面试100题 第二十八题】

题目要求:

  输入一个整数,求该整数的二进制表示中有多少个1.

  例如输入10,由于其二进制表示作为1010,有两个1,因此输出2.

  参考资料:剑指offer第10题、编程之美2.1

题目分析:

  方法1 除2取余法:一个数a%2的值为0或者1,根据是a的二进制表示的最低位为0,则前面结果为0;【取模和取余的效率比较低。】

  方法2 位操作法:例如a = 0011,a先与0x01&操作,得到一个1,然后a右移为0001,再与0x01&操作,又得到一个1,求和为2;

  方法3 查表法:把所有可能的值放到一个数组中,只要输入一个就直接查表即可。这样时间复杂度为O(1)。【这种方法对于8位的比较方便,如果是16位、32位、、、要建立这个数组就非常麻烦了。因此这种方法的算法只适用于需要频繁使用的地方,通过空间复杂度来换取时间复杂度。

  方法4 Hamming weight(汉明重量):先用2位存储邻近2位里面的1的个数,然后合并,用4位表示邻近4位里面的1的个数,再合并、、、

  方法5 减一与法:假如原始数据a = 0011,a&(a-1) = 0001 !=0     0001&(0001-1)=0,则while会进入两次,num=2,即1的个数为2。【只需要运算a表示的二进制中1的个数次就可以求得结果。】  

  方法6 MIT HAKMEM:直接用程序进行讲解。


  方法7 动态建表法:假如输入的数字为32位,则分成4*8位,分别求4个8位二进制中1的个数再求和。

     8位二进制中1的个数的求解方法:动态建表。

     下面说一下动态建表的原理:

     根据奇偶性来分析,对于任意一个正整数n:i).如果它是偶数,那么n的二进制中1的个数与n/2中1的个数是相同的,比如4和2的二进制中都有一个1,6和3的二进制中都有两个1。为啥?因为n是由n/2左移一位而来,而移位并不会增加1的个数;ii).如果n是奇数,那么n的二进制中1的个数是n/2中1的个数+1,比如7的二进制中有三个1,7/2 = 3的二进制中有两个1。为啥?因为当n是奇数时 ,n相当于n/2左移一位再加1。

代码实现:

1.除2取余法

#include <stdio.h>

int count(unsigned x);

int main(void)
{
    printf("%d
",count(0xffff));
    return 0;
}

int count(unsigned u)
{ 
    int num = 0;
    while(u)
    {
        if(u % 2 == 1)
            num++;
        u = u/2;
    }
    return num;
}
View Code

2.位操作法

#include <stdio.h>

int count(unsigned int x);

int main(void)
{
    printf("%d
",count(0xffff));
    return 0;
}

int count(unsigned int u)
{ 
    int num = 0;
    while(u)
    {
        num += u&0x01;
        u >>= 1;
    }
    return num;
}
View Code

3.查表法

#include <stdio.h>

int count_table[256] = 
{
    0,1,1,2,1,2,2,3,1,2,2,3,2,2,2,4,1//...
};
int count(unsigned char x);

int main(void)
{
    printf("%d
",count(0x0f));
    return 0;
}

int count(unsigned char u)
{ 
    return count_table[u];
}
View Code

4.Hamming weight(汉明重量)

#include <stdio.h>

#define M2  0x55555555
#define M4  0x33333333
#define M8  0x0f0f0f0f
#define M16 0x00ff00ff
#define M32 0x0000ffff
int count(unsigned x);

int main(void)
{
    printf("%d
",count(0xffff));
    return 0;
}

int count(unsigned u)
{ 
    u = (u&M2)  + ((u>>1)&M2);
    u = (u&M4)  + ((u>>2)&M4);
    u = (u&M8)  + ((u>>4)&M8);
    u = (u&M16) + ((u>>8)&M16);
    u = (u&M32) + ((u>>16)&M32);

    return u;
}
View Code

5.减一与法

#include <stdio.h>

int count(unsigned x);

int main(void)
{
    printf("%d
",count(0xffff));
    return 0;
}

int count(unsigned u)
{ 
    int num = 0;
    while(u)
    {
        u &= (u-1);
        num++;
    }
    return num;
}
View Code

6.MIT HAKMEM法

#include <stdio.h>

int count(unsigned x);

int main(void)
{
    printf("%d
",count(0x80000000));
    return 0;
}

int count(unsigned u)
{ 
    u = u - (((u >> 1) & 033333333333) + ((u >> 2) & 011111111111));
    u = ((u + (u >> 3)) & 030707070707);
    return u % 63;   
}
View Code

7.动态建表法

#include <stdio.h>

int BitCount3(unsigned int n) ;

int main(void)
{
    printf("bits = %d
",BitCount3(0xffff));
    return 0;
}

int BitCount3(unsigned int n) 
{ 
    // 建表
    unsigned char BitsSetTable256[256] = {0} ; 
    int i;
    unsigned int c =0 ; 
    unsigned char* p;

    // 初始化表 
    for (i =0; i <256; i++) 
    { 
        BitsSetTable256[i] = (i &1) + BitsSetTable256[i /2]; 
    } 

    // 查表
    printf("%d
",&n);
    //这里很重要,先取地址(此时为32位的地址),然后强制转换为char型(8位)的
    p = (unsigned char*) &n ; 
    printf("&p[0] = %d
",&p[0]);
    printf("p[0] = %d
",p[0]);
    printf("&p[1] = %d
",&p[1]);
    printf("p[1] = %d
",p[1]);

    c = BitsSetTable256[p[0]] + 
        BitsSetTable256[p[1]] + 
        BitsSetTable256[p[2]] + 
        BitsSetTable256[p[3]]; 

    return c ; 
}
View Code
原文地址:https://www.cnblogs.com/tractorman/p/4063240.html