一,题目:输入一个整数,求该整数的二进制表达中有多少个1。
例如输入10,由于其二进制表示为1010,有两个1,因此输出2。
二,分析:
这是一道很基本的考查位运算的面试题。
菜鸟思考:利用除法,和取余运算计算出10进制数的二进制表示后,统计1的个数
三,代码
自己测试代码(感觉没问题)
#include <iostream> using namespace std; int count_of_one(int n) { int count=0; while(n) { if(n%2==1) count++; n=n/2; } return count; } int main() { cout<<count_of_one(16)<<endl; return 0; } 查询各方资料,多次杀死脑细胞后,发现:如果为负数的话将导致错误发生改进后:
#include <iostream> using namespace std; int count_of_one(int a) { int count=0; int n; if(a>0) n=a; else n=-a; while(n) { if(n%2==1) count++; n=n/2; } if(a>0) return count; else return count+1; } int main() { cout<<count_of_one(-10)<<endl; return 0; }四,升级版(除法的效率比移位运算要低的多,)
先判断整数的最右边一位是不是1。接着把整数右移一位,原来处于右边第二位的数字现在被移到第一位了,再判断是不是1。
这样每次移动一位,直到这个整数变成0为止。现在的问题变成怎样判断一个整数的最右边一位是不是1了。很简单,如果它和整数1作与运算。由于1除了最右边一位以外,其他所有位都为0。因此如果与运算的结果为1,表示整数的最右边一位是1,否则是0。
得到的代码如下:
int NumberOf1_Solution1(int i) { int count = 0; while(i) { if(i &1) count ++; i = i >> 1; //是相对于二进制数,向右移动一位,等价于除以2 } return count; }
在实际编程中如果可以应尽可能地用移位运算符代替乘除法。
这个思路当输入i是正数时没有问题,但当输入的i是一个负数时,不但不能得到正确的1的个数,还将导致死循环。以负数0x80000000为例,右移一位的时候,并不是简单地把最高位的1移到第二位变成0x40000000,而是0xC0000000。这是因为移位前是个负数,仍然要保证移位后是个负数,因此移位后的最高位会设为1。如果一直做右移运算,最终这个数字就会变成0xFFFFFFFF而陷入死循环。为了避免死循环,我们可以不右移输入的数字i。首先i和1做与运算,判断i的最低位是不是为1。接着把1左移一位得到2,再和i做与运算,就能判断i的次高位是不是1……这样反复左移,每次都能判断i的其中一位是不是1。基于此,我们得到如下代码:
int NumberOf1_Solution2(int i) { int count = 0; unsigned int flag = 1; while(flag) { if(i &flag) count ++; flag = flag << 1; } returncount; }