一、例题(1)
怎么样?初看,是否一头雾水?哈哈,没事,俺也一样😓😓😓,不过,当你看过我的解答定会明朗!
二、例解
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
// 函数用于计算整数n的32位二进制表示中1的个数
int jisuan(int n) {
int count = 0;
while (n) {
// 对于正数或转化后的反码,每做一次n &= (n - 1),就会消去最低位的1
// 直到n变为0,count中的数值就是1的个数
n &= (n - 1);
count++;
}
return count;
}
int main() {
int number;
printf("请输入一个整数: ");
scanf("%d", &number);
int m = jisuan(number);
printf("该数32位二进制表示中1的个数为: %d\n", m);
return 0;
}
但是,此处大家用上面的代码是过不了牛客的!因为这是接口类型的题目,不需要写main函数!使用下面的代码就可以通过了。
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param n int整型
* @return int整型
*/
int NumberOf1(int n ) {
// write code here
int count=0;
while (n)
{
n &= (n - 1);
count++;
}
return count;
}
看不懂?没事,这不还有详解了么😁😁😁,好好看,深刻理解不是问题!
三、详解
这里用到之前讲的知识点:现代计算机在内部进行数值运算时使用补码(通常是指二进制补码)来表示有符号整数。
确实,n &= (n - 1)
这一行代码看起来可能有些抽象,但它实际上是一个非常高效的技巧,用来计算一个整数的二进制表示中有多少个1。这种方法不需要显式地转换整数为二进制字符串,而是直接在整数的二进制表示上进行操作。
让我们通过一个具体的例子来逐步解释这段代码是如何工作的。假设我们有整数 n = 10
,其32位二进制表示为:
10000 0000 0000 0000 0000 0000 0000 1010
我们将使用 n &= (n - 1)
来逐个消除 n
中的1,直到 n
变为0。
步骤1: 开始时,n = 10
,其二进制表示为 0000...0000 1010
。
步骤2: 计算 n - 1
,即 10 - 1 = 9
。9
的二进制表示为 0000...0000 1001
。
步骤3: 使用 n &= (n - 1)
,即 10
和 9
进行按位与操作。这将清除 n
的二进制表示中最右边的1。
n = 0000...0000 1010
n-1 = 0000...0000 1001
---------------------
n &= (n-1) = 0000...0000 1000
现在,n
变为了 8
,其二进制表示为 0000...0000 1000
。
步骤4: 再次执行 n &= (n - 1)
,即 8
和 7
进行按位与操作。
n = 0000...0000 1000
n-1 = 0000...0000 0111
---------------------
n &= (n-1) = 0000...0000 0000
此时,n
变为了 0
,因此循环结束。
计数器更新: 在每次执行 n &= (n - 1)
并且 n
不等于0时,count
都会增加1。对于 n = 10
的情况,count
增加了两次,因此输出结果为2,这正是二进制表示中1的个数。
什么?!还看不懂?
我们可以更详细地解释这个过程。以 n = 10
为例,其二进制表示为 0000...0000 1010
(省略了前面的零)。让我们再次逐步分析:
步骤1:
初始状态:
n = 10
,二进制表示为0000...0000 1010
count = 0
步骤2:
执行 n &= (n - 1)
:
n - 1 = 9
,二进制表示为0000...0000 1001
n &= (n - 1)
结果为0000...0000 1000
count++
,所以count = 1
步骤3:
再次执行 n &= (n - 1)
:
n
现在为8
,二进制表示为0000...0000 1000
n - 1 = 7
,二进制表示为0000...0000 0111
n &= (n - 1)
结果为0000...0000 0000
count++
,所以count = 2
步骤4:
此时 n
已经变成了 0
,所以 n &= (n - 1)
不再执行,循环结束。
最终 count
的值为 2
,这意味着在原始的 n = 10
的二进制表示中,有2个1。
扩展看看呢?
n=5 //0000 0000 0000 0000 0000 0000 0000 0101--5
n-1=4 //0000 0000 0000 0000 0000 0000 0000 0100--4
5&=4 //0000 0000 0000 0000 0000 0000 0000 0100--4
n=4 //0000 0000 0000 0000 0000 0000 0000 0100--4
n-1=4 //0000 0000 0000 0000 0000 0000 0000 0011--3
4&=3 //0000 0000 0000 0000 0000 0000 0000 0000--0
另一个例子:n = 13
二进制表示为 0000...0000 1101
步骤1:
初始化:
n = 13
,二进制表示为0000...0000 1101
count = 0
步骤2:
执行 n &= (n - 1)
:
n - 1 = 12
,二进制表示为0000...0000 1100
n &= (n - 1)
结果为0000...0000 1100
count++
,所以count = 1
步骤3:
再次执行 n &= (n - 1)
:
n
现在为12
,二进制表示为0000...0000 1100
n - 1 = 11
,二进制表示为0000...0000 1011
n &= (n - 1)
结果为0000...0000 1000
count++
,所以count = 2
步骤4:
再次执行 n &= (n - 1)
:
n
现在为8
,二进制表示为0000...0000 1000
n - 1 = 7
,二进制表示为0000...0000 0111
n &= (n - 1)
结果为0000...0000 0000
count++
,所以count = 3
步骤5:
n
已经变成了 0
,循环结束。
最终 count
的值为 3
,意味着 n = 13
的二进制表示中有3个1。
小结
这种技巧之所以有效,是因为 n - 1
会在 n
的二进制表示中翻转最右边的1以及它右边的所有1,使得 n
和 n - 1
的按位与操作会消除最右边的1。这样,每次循环都会消除一个1,直到 n
变为0,此时所有1都被计数完毕。
四、例题(2)
输入一个整数,输出他的二进制,如何实现呢?
例
输入:10
输出:0000 0000 0000 0000 0000 0000 0000 1010
示例代码:
#include <stdio.h>
int main() {
int number;
printf("请输入一个整数: ");
scanf("%d", &number);
// 输出前32个字符作为二进制表示的前缀
for (int i = 31; i >= 0; i--) {
int k = number >> i;
if (k & 1)
printf("1");
else
printf("0");
// 每8位后添加空格以提高可读性
if ((i % 8) == 0 && i != 31)
printf(" ");
}
printf("\n");
return 0;
}
在这个程序中,我们首先读取用户输入的整数。然后,我们通过循环从最高位到最低位逐位检查这个整数。我们使用右移运算符(>>
)来获取每一位的值,再使用按位与运算符(&
)和数字1来判断这一位是否为1。如果是,我们就打印出1;否则,打印出0。
为了提高输出的可读性,每8位之后都会插入一个空格。这是因为通常的32位整数在二进制下被分成4组,每组8位。
如果你想要测试这个代码,可以将它复制到你的C编译器中,编译并运行它。当提示你输入一个整数时,输入10,你将会看到如下的输出:
10000 0000 0000 0000 0000 0000 0000 1010
五、总结
现在,你是否对位操作符有了更深入的理解?哈哈,切勿认为这样就可以了,这方面还有着很多的的知识等着我们去发现和学习,往后需要时忘记了,记得常回来看看!😁😁😁