编程之美:求二进制中1的个数

3 篇文章 0 订阅

1.问题描述

实现一个函数,输入一个无符号整数,输出该数二进制中的1的个数。例如把9表示成二进制是1001,有2位是1,因此如果输入9,该函数输出2

 

2.分析与解法

解法1:利用十进制和二进制相互转化的规则,依次除余操作的结果是否为1  代码如下:

复制代码
int Count1(unsigned int v)
{
    int num = 0;
    
    while(v)
    {
         if(v % 2 == 1)
         {
              num++;  
         }
         v = v/2;
    }
    
    return num;
}
复制代码

 

 

解法2:向右移位操作同样可以达到相同的目的,唯一不同的是,移位之后如何来判断是否有1存在。对于这个问题,举例:10100001,在向右移位的过程中,我们会把最后一位丢弃,因此需要判断最后一位是否为1,这个需要与00000001进行位“与”操作,看结果是否为1,如果为1,则表示当前最后八位最后一位为1,否则为0,解法代码实现如下,时间复杂度为O(log2v)。

复制代码
int Count2(unsigned int v)
{
    unsigned int num = 0;
    
    while(v)
    {
         num += v & 0x01;
         v >>= 1;
    }
    return num;
}
复制代码

 

 

解法3:利用"与"操作,不断清除n的二进制表示中最右边的1,同时累加计数器,直至n为0,这种方法速度比较快,其运算次数与输入n的大小无关,只与n中1的个数有关。如果n的二进制表示中有M个1,那么这个方法只需要循环k次即可,所以其时间复杂度O(M),代码实现如下:

复制代码
int Count3(unsigned int v)
{
    int num = 0;
    
    while(v)
    {
         v &= (v-1);
         num++;
    }
    return num;
}
复制代码

 

 

编程之美同时给出了8bit的情况下,解法4:使用分支操作,解法5:查表法 再计算32bit无符号整数时,需要将32bit切为4部分 然后每部分分别运用解法4解法5下面仅给出代码:

解法4:

复制代码
int Count4(unsigned int v)
{
    int num = 0;
    
    switch(v)
    {
        case 0x0:
             num = 0;
             break;
        case 0x1:
        case 0x2:
        case 0x4:
        case 0x8:
        case 0x10:
        case 0x20:
        case 0x40:
        case 0x80:
             num = 1;
             break;
        case 0x3:
        case 0x6:
        case 0xc:
        case 0x18:
        case 0x30:
        case 0x60:
        case 0xc0:
             num = 2;
             break; 
        //.....
    }
    return num;
}
复制代码

解法5:

复制代码
unsigned int table[256] =     
{        
         0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
         4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8        
};

int CountTable(unsigned int v)
{
    return table[v & 0xff] +
           table[(v >> 8) & 0xff] +
           table[(v >> 16) & 0xff] +
           table[(v >> 24) & 0xff] ;
}
复制代码

 

 

网上还给出了 平行算法,思路:将v写成二进制形式,然后相邻位相加,重复这个过程,直到只剩下一位。以217(11011001)为例,有图有真相,下面的图足以说明一切了。217的二进制表示中有5个1。

 

代码如下:

复制代码
int Count6(unsigned int v) 
{ 
    v = (v & 0x55555555) + ((v >> 1) & 0x55555555) ; 
    v = (v & 0x33333333) + ((v >> 2) & 0x33333333) ; 
    v = (v & 0x0f0f0f0f) + ((v >> 4) & 0x0f0f0f0f) ; 
    v = (v & 0x00ff00ff) + ((v >> 8) & 0x00ff00ff) ; 
    v = (v & 0x0000ffff) + ((v >> 16) & 0x0000ffff) ; 

    return v ; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值