今天开始看《编程之美》了,这是一本评价很高的书,找工作当然少不了它。
部分题目已经在书中给出了答案,这里只是给出我的一些想法,有些也来源于此书,就当作做笔记吧。最后,感谢作者为我们奉献了一本优秀的程序员找工作宝典。
题目介绍:给定一个字节的无符号整型变量,求其二进制表示中“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了。
[编程之美] 2.1 求二进制数中1的个数,布布扣,bubuko.com
原文:http://blog.csdn.net/luofengmacheng/article/details/20362851