我们可以将题目理解为求解一个二进制数中每一位上的数相加,如果是0则相加后对于求和不影响,如果为1则每一位上的1相加后得到的恰好是二进制数中1的个数,这是由于二进制每一位上的最大数字恰好是1所产生的特点(即count++ = count + 1)。
根据以上理解,我们可以得到三种不同的解法。
一、除模法
如果想要得到一个十进制数中每一位上的数,我们可以将这个数不断的(%10)再(/10)直到这个数变为0为止。
例如:12345
12345%10=5,12345/10=1234;————5
1234%10=4,1234/10=123;————4
123%10=3;123/10=12;————3
12%10=2,12/10=1;————2
1%10=1,1/10=0;————1
因此我们可以得到12345每一位上的数分别为:1、2、3、4、5;类比于十进制的方法,我们同理可以得到求解二进制数上每一位数的方法——即:将一个二进制数不断的(%2)再(/2).
例如:15——1111(二进制)
1111%2=1,1111/2=111;——1
111%2=1,111/2=11;——1
11%2=1,11/2=1;——1
1%2=1,1/2=0;——1
我们将每一位上的数相加得到4,即15转换成二进制数共有4个1;
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
int input = 0;
scanf("%d", &input);
int count = 0;
while (input != 0)
{
if ((input % 2) == 1)
count++;
input /= 2;
}
printf("%d", count);
return 0;
}

但是我们又会发现,如果输入的是负数,例如:-1,就会得到以下结果:

这是因为按照上述代码逻辑,input=-1,-1%2=-1,之后input/2=0直接跳出循环,所以count=0。
由于计算机存储数据采用的是补码,例如:-1
源码:10000000000000000000000000000001
反码:11111111111111111111111111111110
补码:11111111111111111111111111111111
所以按理来说,-1的二进制数应该有32个1,但此时却显示0。
那这个方法是不是就失效了呢?
当然不是,只需要稍微修改一下代码也可以完成题目要求
此时我们只需要把input的数据类型改为unsigned int,将-1视为是一个很大的正数(因为其二进制补码在无符号情况下很大)进行运算即可。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
unsigned int input = 0;
scanf("%u", &input);
int count = 0;
while (input != 0)
{
if ((input % 2) == 1)
count++;
input /= 2;
}
printf("%d", count);
return 0;
}

二、位运算——右移法
计算机存储数据采用的是补码形式,通过将二进制数与1进行按位与“&”可以得到二进制数个位数为0还是1,之后再进行右移操作即可将原来的个位数删去,将第二位数变成个位数再次进行按位与操作,以此类推即可得到二进制数中1的个数。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
int input = 0;
scanf("%d", &input);
int count = 0;
for (int i = 0; i < 32; i++)
{
if (((input>>i) & 1) == 1)
count++;
}
printf("%d", count);
return 0;
}


三、位运算——按位与法
在讲解这个方法之前我们先来看一组运算。
假设:n=15;
n&(n-1)=(1111)&(1110)=1110,令n = 1110
n&(n-1)=(1110)&(1101)=1100,令n = 1100
n&(n-1)=(1100)&(1011)=1000,令n = 1000
n&(n-1)=(1000)&(0111)=0000,令n = 0
我们不难发现在执行n&(n-1)操作时,n的最后一个1逐次变成0,直到n=0
因此,利用这种运算方法我们也可以求解出二进制数中1的个数。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
int input = 0;
scanf("%d", &input);
int count = 0;
while (input != 0)
{
input = input & (input - 1);
count++;
}
printf("%d", count);
return 0;
}


无论是正数还是负数,以上三种方法均可以实现。其中第三种方法不是一下就能想到的,它要求对于位运算掌握度比较深。
其中第三种方法n&(n-1)还可以用来求解一个数是否是2的n次方。
因为该运算本质上是将二进制数最后一个1置0,如果这个数是2的n次方,则n&(n-1)==0
732




被折叠的 条评论
为什么被折叠?



