题目:统计二进制中1的个数
要求:
写一个函数返回参数二进制中 1 的个数。
比如: 15 0000 1111 4 个 1
在进行代码编写之前,我们先回忆一下整数的二进制表示形式
整数的二进制表示形式:原码、反码、补码
(整数的存储类型是整型,1个整型是4个字节,32个比特位。第一位是符号位,1代表负数,0代表正数。数据在内存中是以二进制补码的形式进行存储的。)
1.正整数的原码、反码、补码是相同的
原码:根据正整数直接写出二进制序列
2.负整数的原码、反码、补码需要计算
原码:根据正整数直接写出二进制序列
反码:符号位不变,其他位按位取反
补码:反码+1
我们可以首先写出代码的主体部分
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int count_bit_one(int m)
{
int count = 0;//用来统计二进制中1的个数
return count;
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = count_bit_one(n);
printf("%d\n", ret);
return 0;
}
方法一:
二进制想得到它的每一位,我们应该怎么得到呢?
类比十进制得到它的每一位,我们是怎么得到的?
假设1234,我们要怎么得到他的每一位
1234 % 10 =4
1234 / 10 =123
123 % 10 = 3
123 / 10 =12
12 % 10 =2
12 / 10 = 1
1 % 10 = 1
1 / 10 = 0
根据十进制得到他的每一位,我们来模拟一下二进制
假设15,我们要得到他的每一位
15 1111
15 % 2 =1
15 / 2 = 7 0111
7 % 2 = 1
7 / 2 = 3 0011
3 % 2 = 1
3 / 2 = 1 0001
1 % 2 = 0
1 / 2 = 0
以上这段描述,可以用while循环来实现
while (m)
{
if (m % 2 == 1)
count++;
m /= 2;
}
整体代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int count_bit_one(int m)
{
int count = 0;//用来统计二进制中1的个数
while (m)
{
if (m % 2 == 1)
count++;
m /= 2;
}
return count;
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = count_bit_one(n);
printf("%d\n", ret);
return 0;
}
此时此刻,想必聪明的同学,已经想到了,万一输入的数字是负数呢 ?
这时候,我们仔细一想,整数是有符号位的,我们可以传无符号的数字,即使用unsigned。
int count_bit_one(unsigned int m)
完整代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int count_bit_one(unsigned int m)
{
int count = 0;//用来统计二进制中1的个数
while (m)
{
if (m % 2 == 1)
count++;
m /= 2;
}
return count;
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = count_bit_one(n);
printf("%d\n", ret);
return 0;
}
这时候,输入负数,程序也可以得到正确答案了。
方法二:
我们可以通过得到最后一位是什么和右移即可。由学习的知识我们可以得到,可以按位与(&)1来得到最低位。
15 的二进制序列是00000000000000000000000000001111
1 的二进制序列是00000000000000000000000000000001
15 和 1 按位与
& ——对应二进制位有0则为0,两个同时为1,才是1
00000000000000000000000000001111
00000000000000000000000000000001
00000000000000000000000000000001
得到15的二进制序列的最后一位是1
m & 1
然后我们可以根据for循环来不断右移,从而得到最后一位。
int i = 0;
for (i = 0; i < 32; i++)
{
if (((m >> i) & 1) == 1)
count++;
}
完整代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int count_bit_one(int m)
{
int count = 0;//用来统计二进制中1的个数
int i = 0;
for (i = 0; i < 32; i++)
{
if (((m >> i) & 1) == 1)
count++;
}
return count;
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = count_bit_one(n);
printf("%d\n", ret);
return 0;
}
缺陷:不论输入数字是多少,都会循环32次。因此效率比较低。
方法三:
有一个公式: n = n & ( n - 1 ):会让 n 的二进制中最右边的 1 消失
假设 n 为14
n 的二进制序列:00000000000000000000000000001110
n - 1的二进制序列:00000000000000000000000000001101
& ——对应二进制位有0则为0,两个同时为1,才是1
n 为00000000000000000000000000001110
n-1 为 00000000000000000000000000001101
n 为00000000000000000000000000001100
n - 1 为00000000000000000000000000001011
n 为00000000000000000000000000001000
n - 1 为00000000000000000000000000000111
n 为00000000000000000000000000000000
即假如 n 中有 m 个 1 ,则 n = n & ( n - 1 ) 每执行一次,去掉一个 1 ;总共执行 m 次,n 会变为0;
即可以用while循环来判断,直至 n = 0
while (m)
{
m = m & (m - 1);
count++;
}
完整代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int count_bit_one(int m)
{
int count = 0;//用来统计二进制中1的个数
while (m)
{
m = m & (m - 1);
count++;
}
return count;
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = count_bit_one(n);
printf("%d\n", ret);
return 0;
}