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

今天开始看《编程之美》了,这是一本评价很高的书,找工作当然少不了它。

部分题目已经在书中给出了答案,这里只是给出我的一些想法,有些也来源于此书,就当作做笔记吧。最后,感谢作者为我们奉献了一本优秀的程序员找工作宝典。


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

因为题目要求算法尽可能高效,因此,对于书中的解法一大家应该不会考虑。不仅思路简单,而且其中用到的运算也很耗时,比如求余和除法。

大家最应该想到的应该就是解法二了,虽然思路同解法一一样,但使用了更为高效的位运算。

这里要提一下C++中的bitset,假如要求34的二进制表示中“1”的个数,可以这样来求:

bitset<8> b(34);
cout << b.count() << endl;
上面的代码声明了一个8位的bitset变量b,然后调用b的count()函数,它返回的就是这个bitset中被置位的个数。

代码很简洁,但是,如果细看count()函数的代码,就知道,它采用的就是最简单的方式:

size_t
count() const _GLIBCXX_NOEXCEPT
{ return this->_M_do_count(); }

size_t
_M_do_count() const _GLIBCXX_NOEXCEPT
{
    size_t __result = 0;
    for (size_t __i = 0; __i < _Nw; __i++)
        __result += __builtin_popcountl(_M_w[__i]);
    return __result;
}
count()会调用_M_do_count(),_M_do_count()会依次检测它的所有位。因此,代码虽然简洁,但是效率并不高。

解法三的想法很好,前面的思路基本都是遍历这个整型变量的所有位,而解法三期望复杂度只与“1”的个数有关。

int Count(BYTE v)
{
    int num = 0;

    while(v) {
        v &= v - 1;
        ++num;
    }

    return num;
}
v与v - 1进行与操作可以将v的最后一个“1”变成0,从而得到有1个“1”。这种方式还是很巧妙的。

至于解法四和解法五都采用了用空间换时间的策略,只不过解法五更加直接。


扩展问题:

1. 如果变量是32位的DWORD,你会使用上述的哪一个算法,或者改进哪一个算法?

解法一的时间复杂度是O(logN),也就是位数,而且使用了较慢的四则运算。

解法二的时间复杂度也是O(logN),但使用的是位运算,效率较第一种高。

解法三的时间复杂度是该数中“1”的个数,且使用位运算,效率更高。

解法四用的case分支语句,时间复杂度依赖于给定的数。

解法五很直接,时间复杂度为O(1)。

当位数较多时,肯定是解法三更好。如果位数较少的话,解法四和五也可以。这里是32位,用解法三。

2. 给定两个正整数A和B,把A变成B需要改变多少位?也就是说,整数A和B的二进制表示中有多少位是不同的?

将A和B异或,当对应的位不同时,结果为1,然后求结果中有多少个“1”就OK了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值