统计二进制中1的个数是一道经典的面试题,常常被用来考察候选人对位操作和算法的理解。这个问题的来源可以追溯到计算机科学领域的早期。
第一种方法的思路是通过循环和除以2的操作来逐位判断一个整数的二进制表示中是否为1,并计算1的个数。下面是代码的简要思路说明:
- 首先,定义了一个函数
count_bit_one
,该函数接受一个无符号整数input
作为参数,用于计算二进制表示中1的个数。 - 在
count_bit_one
函数中,初始化计数器count
为0。 - 进入循环,当
input
不为0时,执行以下操作:
- 判断
input
除以2的余数是否为1,即判断二进制表示的最右边一位是否为1。 - 如果余数为1,则将计数器
count
自增1,表示找到了一个1。 - 将
input
除以2,相当于将二进制表示向右移动一位,去掉最右边的一位。
- 循环结束后,返回计数器
count
的值,即为二进制表示中1的个数。
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//计算1的个数
int count_bit_one(unsigned int input) {
int count = 0;
while (input) {
if (input % 2 == 1)
count++;
input = input / 2;
}
return count;
}
int main()
{
int input = 0; //存放读入的数字
printf("请输入一个整数:\n");
scanf("%d", &input);
int count = count_bit_one(input);//用于计算1的个数,并返回
printf("%d转为二进制一共有%d个1\n", input, count);
return 0;
}
需要注意的是:
count_bit_one函数中,使用 unsigned int类型是为了确保输入的整数是非负的。由于二进制表示中的最高位用来表示符号(0表示正数,1表示负数),因此使用unsigned int可以确保输入的整数只包含正数部分,而不会有负数的情况。
如果使用int类型,输入一个负数时,循环判断的条件while (input)会一直成立,导致无限循环。而使用unsigned int类型,当输入负数时,负数是以补码的形式存储的,其二进制表示的最高位为1,循环条件while (input)会在经过一次循环后判断为假,退出循环。
第二种方法是使用了按位与运算符(&
)来判断二进制表示的最右边一位是否为1。按位与运算符将两个操作数的对应位进行逻辑与运算,只有当两个操作数的对应位都为1时,结果位才为1,否则为0。
在循环中,我们将input
与1进行按位与运算,判断最右边一位是否为1。如果结果为1,则计数器count
自增1,表示找到了一个1。然后,将input
右移1位,相当于将二进制表示向右移动一位,去掉最右边的一位。
循环结束后,返回计数器count
的值,即为二进制表示中1的个数。
你可以调用这个函数并传入一个无符号整数,它将返回该整数的二进制表示中1的个数。
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//按位与求二进制中1的个数
int count_bit_one(unsigned int input) {
int count = 0;
while (input) {
if (input & 1) //最右位是1,计数器加1
count++;
input >>= 1; // 右移1位
}
return count;
}
int main()
{
int input = 0; //存放读入的数字
printf("请输入一个整数:\n");
scanf("%d", &input);
int count = count_bit_one(input);//用于计算1的个数,并返回
printf("%d转为二进制一共有%d个1\n", input, count);
return 0;
}
第三种方法也是最经典的方法:"Brian Kernighan's Algorithm"(布赖恩·克尼根算法)
该算法通过不断将整数的最右边的1置为0,直到整数变为0,同时计数置为1的次数。
布赖恩·克尼根算法(Brian Kernighan's Algorithm)是一种用于计算二进制表示中1的个数的经典算法。该算法由计算机科学家布赖恩·克尼根(Brian Kernighan)提出,并被广泛应用于计算机领域。
算法的基本思想是利用位操作技巧,通过不断将整数的最右边的1置为0来计算1的个数。它的核心观察结果是:对于任意的整数n,n与(n-1)进行按位与运算,可以将n的二进制表示中的最右边的1置为0。
以下是布赖恩·克尼根算法的步骤:
- 初始化一个计数器count为0。
- 循环直到整数n变为0:
- 将n与(n-1)进行按位与运算,结果赋值给n。
- 计数器count增加1。
- 返回计数器count的值,即为二进制表示中1的个数。
在每次循环中,n与(n-1)的按位与运算会将n的最右边的1及其右边的所有位都置为0,而保持其他位的值不变。通过不断执行这个操作,直到n变为0,我们可以计算出二进制表示中1的个数。
举个例子:
当我们使用布赖恩·克尼根算法计算二进制中1的个数时,让我们以一个整数10(二进制表示为1010)为例。下面是使用该算法计算1的个数的步骤:
- 初始化计数器count为0。
- 循环直到整数n变为0:
- 将n与(n-1)进行按位与运算,结果赋值给n。
- 计数器count增加1。
初始状态:
- n = 10 (二进制:1010)
- n - 1 = 9 (二进制:1001)
- count = 0
第一次循环:
- n = n & (n-1) = 10 & 9 = 8 (二进制:1000)
- n - 1 = 7 (二进制:0111)
- count = 1
第二次循环:
- n = n & (n-1) = 8 & 7 = 0 (二进制:0000)
- count = 2
循环结束,最终结果:
- count = 2
因此,整数10的二进制表示中有2个1。
这个例子展示了布赖恩·克尼根算法如何通过不断将整数的最右边的1置为0来计算二进制中1的个数。通过按位与运算和计数器的累加,我们可以高效地获得结果。
布赖恩·克尼根算法的优点在于,它的运行时间仅取决于整数中1的个数,而不是整数的位数。因此,对于具有大量位的整数,该算法比遍历所有位的方法更高效。
以下是使用布赖恩·克尼根算法计算二进制中1的个数的示例代码:
int count_bit_one(unsigned int input) {
int count = 0;
while (input) {
input = input & (input - 1);
count++;
}
return count;
}
int main()
{
int input = 0; //存放读入的数字
printf("请输入一个整数:\n");
scanf("%d", &input);
int count = count_bit_one(input);//用于计算1的个数,并返回
printf("%d转为二进制一共有%d个1\n", input, count);
return 0;
}
在这个算法中,我们使用了一个关键的位操作:input = input & (input - 1)
。这个操作会将input
的最右边的1置为0,同时保留其他位的值不变。通过不断执行这个操作,直到input
变为0,我们可以计算出二进制表示中1的个数。
在循环中,我们将input
与(input - 1)
进行按位与运算,得到的结果赋值给input
。这个操作会将input
的最右边的1及其右边的所有位都置为0。因此,每执行一次这个操作,就相当于消去了一个1,计数器count
增加1。
循环结束后,返回计数器count
的值,即为二进制表示中1的个数。