# 面试题: 求一个整数二进制中 1 的个数?
/*
题目: 请实现一个函数,输入一个整数,输出该数二进制表示中 1 的个数。
例如把 9 表示为二进制是 1001, 有两个 1;因此,如果输入 9,则输出 2;
*/
题目分析:
学过计算机机基础的同学应该都知道,在计算机中的存储都是以二进制形式存在的! 那么求出一个整数
二进制中 1 的个数,当然不在话下了, 看到这道题的的时候应该都是信息满满; 注意: 当面试的时候遇到这道题,
就得仔细思考一下了,面试官不会那么无聊吧! 无非就是把整数变成二进制,用计数器加出来其中 1 的个数嘛1 !
对,没问题,那么问题就在于实现时的细节了,老话常说:细节决定成败!
算法选择:
已经很明确了,选择计数器,循环; 那么我们来看下面实现的几个版本有什么不同!
代码实现: @算法 (一)>>>>将num 的二进制和1进行按位&, 则&操作两个都为 1 结果才为1 ,否则为 0;
用每次结果为1时 ,count+1;
int NumberOf1(num)
{
int count = 0;//计数器
while(num)
{/*
这块的num&1 相当于 num%2;num>>1相当于num / 2;
但是移位运算要比除法运算的效率高得多!
*/
if(num & 1)
count++;
num = num>>1;
}
return count;
}
代码反思: 代码写到这,并不代表就完事了,还要测试一下啊,多测试几组数据你就会发现,输入 -1,的时候会死循
环,这是因为负数的符号位是 1 (这个在计算机基础里面有); 并不是你把最高位的 1 右移之后就变成 0 了;例
如: 10000000 00000000 0000000 00000001 这是(-1)
11000000 00000000 0000000 00000000 (-1)右移一位之后最高位的 1 不变;
所以程序会死循环;那么就有了下面的改进:
代码实现: @算法(二) 设置一个标志位,移动标志位不移动 num 本身;即 另flag=1,然后num 和 flag按位&
如果&的结果是1 .则count++; 每次&完,flag左移一位;就解决了负数问题;例如:进行一次循环
num = -1: 10000000 00000000 0000000 00000001 ;
flag = 1 : 00000000 00000000 0000000 00000001 ;
num&flag: 00000000 00000000 0000000 00000001 ;
flag<<=1: 00000000 00000000 0000000 00000010 ;
然后进行下次循环flag和num 的第31位进行比较,一次类推循环32次!
int NumberOf1(num)
{
int count = 0;//计数器
int flag = 1;
while(flag)//循环32次后flag自动变为0;
{
if(num & flag)
count++;
flag = flag << 1;
}
return count;
}
@与此类似的for循环32次的代码如下:
int NumberOf1(num)
{
int count = 0;//计数器
int i =0;
for(;i<32;i++)
{
if(num & 1)
count++;
num>>=1;
}
return count;
}
ps: 写到这里,是不是觉得应该完了吧! 就这么一个题! but,答案是否定的, 假面试官告诉你,觉得还有有些麻
烦,让你改一下,num中有几个 1 就循环几次;
你该怎么做?
/*
这下就得好好开动脑子了,这才有些算法的味道出来了;
那么我们就开始分析吧! 首先,先例一个数出来吧,比如 7;二进制为: .....0111; 前面的0省略了,反正人家就要
求有几个1循环几次(别忘了负数)
减去一个 1试试,变成: .....0110; 再将num-1 和 num 按位&一下是不是把右
边的一个1变为了 0; 那么规律来了,只要num不为0,那么它总有一位是1;那就count++;
接着将 num & num-1; 因
为你已经count++了,所以就应该让num少一个 1 ; 循环起来;
*/
代码实现:
@算法(三) 有几个1 循环几次;
int NumberOf1(num)
{
int count = 0;//计数器
while(num)
{
++count;
num = num & (num - 1);
}
return count;
}
over !
thanks!