题目:请实现一个函数,输入一个整数,输出该二进制表示中1的个数。例如把9表示成二进制是1001,有2位是1,因此如果输入9,该函数输出2。
常规的解题思路:
可以把n和1做与运算,如果结果为1,那么n的最后一位为1,否则为0,然后n右移1位,再和1做与运算,直到判断完n的所有位。
int statisticsNums(int n)
{
int sum=0;
while(n)
{
if(n&1)
{
sum++;
}
n=n>>1;
}
return sum;
}
我们来看一下左移和右移运算的法则:
左移(<<):左移n位,高位舍弃n位,低位补n个0
右移(>>):右移n位,低位舍弃n位,当数为正数时,高位补n个0,当数为负数时,高位补n个1。因为在计算机组成原理中,符号位1表示负数,符号位0表示正数。
举两个栗子:
1.3<<2
假设整数3为int类型,那么它占4个字节,也就是有32位,表示如下:
左移两位后结果应该是这样:
3<<2=12
2.9>>1
9的二进制位:
9右移1位:
9>>1=4
可以看出来,在数学意义上,左移n位代表乘以2的n次方,右移n位代表除以2的n次方。
我们再来看一下计算机组成原理中是如何表示一个负数的。
在计算机中为了减少操作,一般用数的补码来表示一个数。
正数的补码=原码
负数的补码=负数正值的原码取反+1
比如计算-9的补码过程:
所以-9的二进制补码用16进制表示为0xFFFFFFF7,
回到上面的问题,假设给函数传入的参数为-9,那么当-9经过4次右移1位后,结果将变成
结果用16进制表示就是0xFFFFFFFF,所以程序最终会陷入死循环。
为了避免死循环的出现,我们可以不让n移动,而每次让1左移1位,这样结果也是一样的。
int statisticsNums(int n)
{
int sum=0;
unsigned int flag=1;
while(flag)
{
if(n&flag)
sum++;
flag=flag<<1;
}
return sum;
}