求二进制数中1的个数

  <<编程之美>>中有这么个题目:对于一个字节的无符号整形变量,求其二进制表达形式中“1”的个数。算法

  基础算法:展转相除法编程

  展转相除法是十进制采用的算法,该算法以下:spa

int count_one_cnt(int value)
{
    int cnt = 0;
    while (value)
    {
        if (value % 2 == 1)
        {
            cnt += 1;
        }
        value /= 2;
    }
    return cnt;
}

  上述算法的时间复杂度O(logV)(V是value比特数),上述算法的操做对象的级别是“数”级别的,题目的要求是“位”级别的,所以若是可以经过位运行完成题目要求功能,将会更加高效:1)除法采用右移实现;2)经过与1位与运算判断是否有1的存在。code

int count_one_cnt(int value)
{
    int cnt = 0;
    while (value)
    {
        cnt += value & 1;
        value >>= 1;
    }
    return cnt;
}

  位与法:对象

  上面的两个算法时间复杂度均依赖于欲求的数据类型在存储空间中占有的比特数,位与法算法复杂度则仅与待求数据中1的个数相关。首先,观察某个数a与a-1:在二进制下,a-1在最右端加上1就等于a,a与a-1进行位与运算则会消除最右边的1,能够从如下角度理解;blog

  1)a与a-1仅差一个1,而这个1是从a-1最低端加上便等于aip

  2)假设a最低端出现的位置pos,pos后面均是0,所以与b位与运算,pos位置后面均是0字符串

  3)a-1的对应pos位置,必定是0。假设a-1在pos位置等于1,a-1后面必然均是0,不然a-1将会大于a,但根据1)a等于a-1处最低端位加1,而a-1从pos位置开始均是0,在a-1最低端位加1获得的a,在pos位置到最低端必定有一个1,与2)矛盾。get

  虽然理解起来有些绕弯,可是代码倒是很是简洁的:it

int count_one_cnt(int value)
{
    int cnt = 0;
    while (value)
    {
        value &= value-1;
        cnt += 1;
    }
    return cnt;
}

  位图法:

  <<编程之美>>提到了这种方法:8bit的无符号整数的范围是[0, 255),所以直接将256个数还有的1位数作出表的形式,以时间换空间~实现方法在此省略。

  Hamming weight方法:

  Hamming weight:是指在给定的字符串中,全部不等于0的字符的个数,在一串二进制的字符串中,等于该二进制中1的个数。Hamming weight方法的一种快速实现,采用了“隔位相加”的方法:相邻位相加,并存在与当前位置中。算法过程以下(来源与维基百科):

能够从下图快速理解算法的执行过程:

  

实现方法在此不一一列出,有兴趣能够参考维基百科中给出了三种实现方法~

相关文章
相关标签/搜索