二进制中1的个数的算法

思路: 先判断整数二进制表示中最右一位是否为1,再把输入的整数右移一位,再判断最右一位是否为1。直到整个数变为零。

将整数和1做位与运算看结果是否为0即可判断该整数最右一位是否为1。与运算结果为1时,表示该整数最右一位是1。

1、普通算法:

int NumberOf1( int n)
{
    int count =0 ; // 计数器
    while (n)
    {
        if(n & 1) // 当前位是1
            ++count ; // 计数器加1
        n >>=1 ; // 移位
    }
    return count ;
}
上面的程序会被问两个问题:
       1、整数右移一位和把整数除以2在数学上是等价的,上面的代码中可以把右移运算换成除以2吗?
	答案是否定的!!!因为除法的效率比移位运算要低的多。
	2、若是个负数右移会出项什么情况?(如0X80000000)
	解析:负数移位后仍然是负数,移位后最高位会设为1。0X80000000右移一位是0XC0000000并不是0X40000000。如果,一直将一个负数做右移运算,那么这个数		      字最终将会变成0xFFFFFFFF而陷入死循环。

为避免死循环,我们可以考虑不右移输入数字。
 
 
     先将n和1做与运算,判断最低位是不是为1。接着把1左移一位得到2,再把n做与运算,就能判断n的次低位是不是1……如此反复左移,每次都能判断n的其中一位是否为1.
int BitCount1( int n)
{
    int count =0 ; // 计数器
    unsigned int flag=1;
    while(flag)
    {
	if(n & flag)
	     count++;
	flag = flag << 1;
    }
    return count;
}


 
 
 
2、快速算法(s)
将一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0。那么一个整数的二进制表示中有多少个1,就可以进行多少次这样的操作。
int BitCount2( int n)
{
    unsigned int count =0 ;
    for (count =0; n; ++count)
    {
        n &= (n -1); // 清除最低位的1
    }
    return count ;
}


 
为什么n &= (n – 1)能清除最右边的1呢?因为从二进制的角度讲,n相当于在n - 1的最低位加上1。举个例子,8(1000)= 7(0111)+ 1(0001),所以8 & 7 = (1000)&(0111)= 0(0000),清除了8最右边的1(其实就是最高位的1,因为8的二进制中只有一个1)。再比如7(0111)= 6(0110)+ 1(0001),所以7 & 6 = (0111)&(0110)= 6(0110),清除了7的二进制表示中最右边的1(也就是最低位的1)。
3、平行算法   【精辟】
int BitCount4(unsigned int n) 
{ 
    n = (n &0x55555555) + ((n >>1) &0x55555555) ; 
    n = (n &0x33333333) + ((n >>2) &0x33333333) ; 
    n = (n &0x0f0f0f0f) + ((n >>4) &0x0f0f0f0f) ; 
    n = (n &0x00ff00ff) + ((n >>8) &0x00ff00ff) ; 
    n = (n &0x0000ffff) + ((n >>16) &0x0000ffff) ; 

    return n ; 
}

接下来开始分析改算法是如何实现二进制表示中1的个数统计的,为了方便理解,我们把代码改成如下的形式:
int func(unsigned int i)
{
    unsigned int temp = i;
    temp = (temp & 0x55555555) + ((temp>> 1) & 0x55555555);  //temp相邻位相加  (32位二进制位01010101010101010101010101010101)
    temp = (temp & 0x33333333) + ((temp >> 2) & 0x33333333);  //temp相邻(以2为单位)相加(32位二进制位00110011001100110011001100110011)
    temp = (temp & 0x0f0f0f0f) + ((temp>> 4) & 0x0f0f0f0f);    //temp相邻(以4为单位)相加(32位二进制位00110011001100110011001100110011)
    temp = (temp & 0xff00ff) + ((temp>> 8) & 0xff00ff);       //temp相邻(以8为单位)相加(32位二进制00001111000011110000111100001111)
    temp = (temp & 0xffff) + ((temp>> 16) & 0xffff) ;          //temp相邻(以16为单位)相加(32位二进制11111111111111111111111111111111)
    return temp;
}

       temp相邻位相加:相加原理若相邻的两个数为00则结果为00,   相邻的两个数为01或10则结果为01,相邻两个数为11则结果为10(十进制是2),也就是先小范围统计每两位中1的个数,后面的步骤在累计有多少个1.

0x11530828的二进制表示如下:
0001  0001 1001 0011 0000 1000 0010 1000;
0  1    0  1   1  1   0  2   0  0   1  0   0  1   1  0;
  1         1       2      2        0      1        1      1;
        2                 4                1                    2 
                  6                                    3
                                    9

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值