一、问题与分析
1、问题
求解32位int整数的二进制表示中1的个数。
输入一个整数,输出其二进制表示中1的个数,负数用其补码表示。
2、分析
对于输入的数,我们可以直接认为其是用二进制表示的,不需要我们对数字做额外处理。只需要直接对数字进行1个数的判定。
二、两种解决方法
1、减一位与
#include <stdio.h>
// 将x转化为二进制数,然后计算该二进制数中含有的1的个数
int func(int x)
{
int countx = 0;
while(x)
{
countx++;
x = x&(x-1);
}
return countx;
}
// main function
int main()
{
printf("%d\n",func(-1));// 输出32
return 0;
}
// 判断是的补码,计算机中数字以补码形式存储,这里说的二进制数中含有1的个数认为是补码中1的个数
-1:1111 1111 1111 1111 1111 1111 1111 1111
为了理解这个算法的核心,需要理解以下两个操作:
1)当一个数被减1时,它最右边的那个值为1的bit将变为0,同时其右边的所有的bit都会变成1。
2)每次执行 x&(x-1) 的作用是把ⅹ对应的二进制数中的最后一位1变成0(图中写错了)。因此,循环执行这个操作直到ⅹ等于0的时候,循环的次数就是x对应的二进制数中1的个数。
2、右移位与
对于输入一个数的二进制,有多少个1,可以进行右移,判定右移尾位上的数字是否为1,由于int类型一共有32位,移位次数为32次,可以进行如下判定。
如果不进行移位次数判定,则是因为,正数右移补0不同,负数右移时,最高位补位为1。这样的话,就会有无数个1,造成死循环。
代码如下:
#include <stdio.h>
// 将x转化为二进制数,然后计算该二进制数中含有的1的个数
int func(int n)
{
int res=0;
int cnt=32;
while(n!=0&&(cnt--)!=0)
{
res+=n&1;// 对最后一位是否为1进行计数
n=n>>1;
}
return res;
}
// main function
int main()
{
printf("%d\n",func(-1));// 输出32
return 0;
}
3、左移位与
正数和负数左移时,最低位都补0。
有第二种方法可知,对于右移数字n,负数时会造成死循环。因此,我们尝试左移:
代码如下:
#include <stdio.h>
// 将x转化为二进制数,然后计算该二进制数中含有的1的个数
int func(int n)
{
int res=0;
int flage=1;
while(flage!=0){
if(flage&n)
res++;
flage=flage<<1;
}
return res;
}
// main function
int main()
{
printf("%d\n",func(-1));// 输出32
return 0;
}
四、补充知识
计算机中二进制是以补码的形式存储的,注意第一位最高位是符号位,正的为0,负的为1。如果是正的,补码就是原码,基本不用考虑额外的情况;如果是负的,要以补码的形式,首先把原码取反,再加1就得到补码!!!!