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

本文介绍了几种计算字节中二进制‘1’的数量的方法,并对比了它们的时间和空间复杂度。提出了通过移位操作和利用预存表格的算法,还讨论了这些算法如何应用于不同大小的数据类型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:对于一个字节的无符号整形变量,求其二进制表示中“1”的个数,要求算法的执行效率尽可能高。


刚看到这个题,首先想到的就是对二进制数进行向移位操作,看末位是不是1。这个即为书中给出的第二种方案:

/* 方案2,移位操作*/ 
int Count(BYTE v){
    int num = 0;
    while(v){
        num += v & 0x01; //与000000001进行与操作,结果为1说明末位为1,num+1 
        v >>= 1;         //v向右移1位,直到变为全0 
    }
} 

该方案与方案1的核心思想差不多,用BYTE v去除以2,就是向右移1位,余数即为移出的位是0还是1,如果移出是1,则num+1。这种移位思想,算法复杂度为O(log2v),即v的二进制表示的位数,如果v为10000000,就比较糟糕了,要算8次才能算完。如果复杂度只与v中的1的数量相关,就会好很多。

想到之前做的题目中http://blog.youkuaiyun.com/kimili1987/article/details/8034466,有一个是怎么判断一个数是2^n,事实上,这个数就是只有一个1。方法是,对于数v,判断v & (v-1)是否为0,为0的话则证明有一个1。据此进行扩展,如果有多个1的话,可以逐次使用该方法进行判断,每次会削减掉v中最高位的1,直至最后v为0,判断的次数则为总的1的数量。

/*方案3,判断1的数量*/
int Count(BYTE v){
    int num=0;
    while(v){
        v &= (v-1);     //每次与v-1做与操作,削减掉最高位的1 
        num ++;
    }
} 

方案5的思路很有意思,因为只是一个BYTE的整数,范围为0~255,索性预存一个256个数的数组countTable[256],保存每个数对应的1的个数会是多少,例如countTable[8]=3,然后直接返回countTable[v]就可以得到1的个数了。时间复杂度O(1),空间复杂度是最高的O(256)。这是一个典型的空间换时间的算法,实现虽简单,但是其给出的思想很重要:可以通过牺牲一定的空间,来换取高的时间效率!

扩展问题:

扩展问题1:如果变量v变为32位DWORD,那么使用哪种方案?

对于扩展问题1,当v变为DWORD的时候,方法5可能不太合适了,需要空间过于庞大。而方案3依旧非常适合。这一点也说明了一个算法对于输入数据的适应性问题。根据算法复杂度对哪些变量敏感,不同的算法对输入数据的适应性有所不同。

扩展问题2:给定两个正整数A,B。判断A和B中有多少位是不同的?

1)最直接的方式,A和B每一位都做异或操作,如果为1,num+1,复杂度O(较大位数{A,B})。

2)对A和B做异或,之后再数其中1的数量,Count(A ^ B),复杂度O(1+A和B位数不同的数量)。


这是编程之美书中指出的一个童鞋给出的更好的一些解法

http://blog.youkuaiyun.com/justpub/article/details/2292823


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值