位操作符实战!

一、例题(1)

二进制中1的个数_牛客题霸_牛客网 (nowcoder.com)icon-default.png?t=N7T8https://www.nowcoder.com/practice/8ee967e43c2c4ec193b040ea7fbb10b8?tpId=13&tqId=11164&ru=/exam/oj

怎么样?初看,是否一头雾水?哈哈,没事,俺也一样😓😓😓,不过,当你看过我的解答定会明朗!

二、例解 

#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 = 99 的二进制表示为 0000...0000 1001

步骤3: 使用 n &= (n - 1),即 109 进行按位与操作。这将清除 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),即 87 进行按位与操作。

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,使得 nn - 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

五、总结 

现在,你是否对位操作符有了更深入的理解?哈哈,切勿认为这样就可以了,这方面还有着很多的的知识等着我们去发现和学习,往后需要时忘记了,记得常回来看看!😁😁😁

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小米里的大麦

您的支持是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值