面试题 10,二进制中1的个数(取整数中某一位的经典方法)

这道题我原本的思路是,对于输入的数字InputValue,每次模除2,如果为1,计数器加1,将InputValue右移1位,重复直到InputValue变为0。
这样做的弱点在于:
(1) 如果InputValue是负数,其采用的是反码表示,比如11111111 11111111 11111111 11111101表示的是-2(见负数的存储方式相关资料,http://book.51cto.com/art/201102/245131.htm)。这个时候将这个负数右移一位,为了保证右移后仍然是负数,其最高位上仍然为1(见上面那个括号中的参考资料),因此移如果一直右移一个负数InputValue,最后的结果是11111111 11111111 11111111 11111111,陷入死循环。而且题目里面明确说:“输出该数二进制表示中1的个数”,因此,需要将负数的二进制表示考虑在内。
(2) %2的效率很低,如果想取最低位,有更高效的方法:"&1"
更改后的思路如下,我们不再右移InputValue,而是每次将1左移一位,这样可以挨个取InputValue的每一位。
代码如下:
int OutputAmountofOne(const int inputValue){ insigned int flag = 1; int count = 0; while(flag){ if(inputValue & flag) count++; flag = flag << 1; } return count; }
关于flag是定义为int 还是 unsigned int格式,经过实验和资料查找。发现两种格式下结果一致。
个人分析,原因在于,int和unsigned int在内存存储上一样,只是输出是会根据其类型而改变。同样的11111111 11111111 11111111 11111111,在int被解释为-1,在unsigned int就被认为是2^32-1。而在这段代码中,我们只在二进制上左移flag,并不涉及输出。因此结果一样。
这个方法的时间复杂度为O(N),N为InputValue的位数。
而实际上,有O(logN)的解法,也就是说,不用遍历InputValue的每一位,就可以统计出1的个数。
思路:例如,对于二进制数1000,若减1,则变为0111,就是说,对于二进制的减1,就是从末尾开始寻找1,寻找的过程中,凡是0位都变成1。直到寻找到第一个1,把它变为0,则这次减1运算完毕。
对于数1100,对其减1,变为1011,将此结果和原数1100做&操作,得到1000,我们发现末尾的第一个1就没有了。重复这个动作,直到结果变成0,这个减1动作的重复次数,就是1的个数。
代码:
int OutputAmountofOneLogN(const int inputValue){ int value = inputValue; int count = 0; while(value){ count++; int temp = value - 1; value = temp & value; } return count; }
------------------------------------------------
Felix原创,转载请注明出处,感谢博客园!
posted on 2013-11-29 17:47 Felix Fang 阅读(2285) 评论(0) 收藏 举报
浙公网安备 33010602011771号