首先,我们知道求十进制中的每一位数,是拿那个十进制数模10(%10),拿出最后一位.
那么,同理,二进制的每一位数,自然也可以通过二进制数模2(%2),每拿出一位判断.
第一种方法:模2运算
#include<stdio.h>
int number(int n) {
int count = 0;
while (n) {
if (n % 2 == 1) {
count++;
}
n = n / 2;
}
return count;
}
int main() {
int n = 0;
scanf("%d", &n);
int ret = number(n);
printf("%d", ret);
return 0;
}
例如:一个数在内存中以补码的形式存在
15的二进制为00000000 00000000 00000000 00001111(正数原码、反码、补码相同)
注意:该代码有一定的局限性,比如对于负数就有问题,如n=-1的结果就会打印0,但是我们知道-1在内存中以补码的形式存放,应该是32个1
如:-1 原码:10000000 00000000 00000000 00000001
反码:11111111 11111111 11111111 11111110
补码:11111111 11111111 11111111 11111111
对此我们可以进行改进,如下:
#include<stdio.h>
int number(unsigned int n) {
int count = 0;
while (n) {
if (n % 2 == 1) {
count++;
}
n = n / 2;
}
return count;
}
int main() {
int n = 0;
scanf("%d", &n);
int ret = number(n);
printf("%d", ret);
return 0;
}
可以将输入的n当作进行传参数时当作正数判断,所以上述我写成了unsigned int
第二种方法:移位运算
- 如果拿一个二进制0同1相与,得到的就是0,反之亦然,所以要求1的个数,只需要不停将最后一位同1相与即可
- 使用以为操作符(>>),每次移动i位和1相与,一种只需要移动32位
代码实现:
#include<stdio.h>
int number(int n) {
int count = 0;
int i = 0;
for (i = 0; i < 32; i++) {
if (((n >> i) & 1) == 1)
count++;
}
return count;
}
int main() {
int n = 0;
scanf("%d", &n);
int ret = number(n);
printf("%d", ret);
return 0;
}
注意:该代码也有一定的缺陷,因为必须循环32次
第三种方法:可以使用n = n & (n - 1)
13
1101 n(省略了前面一些比特位)
1100 n - 1
1100 n(得到新的n)
1011 n - 1
1000 n
0111 n - 1
0000 n
代码实现:
#include<stdio.h>
int number(int n) {
int count = 0;
while (n) {
n = n & (n - 1);
count++;
}
return count;
}
int main() {
int n = 0;
scanf("%d", &n);
int ret = number(n);
printf("%d", ret);
return 0;
}
注意:该方法达到了最优的效果,一个数里面只要有多少个1就只需要循环多少次