位移运算题目

01-求一个整数存储在内存中的二进制中1的个数

法一:循环遍历

  • 问题引入

以在十进制中,求某一个数字n中的某个数字m的数量,来引入二进制中求1的个数。

        要求一个十进制数n中某一位数字m的个数,只需要让n对10取模与m比较、再让n = n / 10,反复循环,直到n等于0即可。 由此推断出求一个整数存储在内存中的二进制中1的个数的求法: 

代码实现:

int main () {
    int i;
    int cut = 0;
    scanf("%d", &i);
    while (i) {
        if (i % 2 == 1) {
            cut++;
        }
        i = i / 2;
    }
    printf("%d\n", cut);
    return 0;
}
// 15 -> 4

        上面这种方法看似可以,但其实是有问题的,它不能求负数二进制中1的个数;

        以-1举例:我们知道-1的二进制是全1,所以计算结果应该是32,但是如果我们用上面的方法求出来的结果会是0,因为在第一次循环中-1/2等于0会导致直接退出循环;

优化如下:

        可以将i赋值给一个无符号整数u_i,如果输入的是一个负数的话,它在赋值的时候会发生隐式类型转换,使得u_i变成一个非常大的正数,然后我们直接对u_i求二进制1的个数即可:

int main () {
    int i;
    size_t u_i;
    int cut = 0;
    scanf("%d", &i);
    u_i = i;
    printf("sizeof(u_i)=%d\n", sizeof (u_i)); // 8
    while (u_i) {
        if (u_i % 2 == 1) {
            cut++;
        }
        u_i = u_i / 2;
    }
    printf("%d\n", cut);
    return 0;
}
// -1 -> 64

法二:移位相与 

        思路分析:一个数按位&(与上)一个1得到的结果就是该数最低二进制位所代表的数,同时又知道右移操作符可以让一个数的任意二进制位来到最后一位;所以就可以利用右移+按位与,再配合循环来实现要求:

int main() {
    int i;
    scanf("%d", &i);
    int cut=0;
    int sum=0;
    for (int j = 0; j < 32; ++j) { // 32位机器为例
        sum++;
        if(((i >> j) & 1) == 1) {
            cut++;
        }

    }
    printf("cut=%d,sum=%d\n", cut,sum);
    return 0;
}

         上面for循环的作用是让i的每一位二进制位都与1按位求与,如果求得的结果为1(即对应二进制位为1)就让cut++;由于这是直接对内存中的二进制位进行操作,所以不用担心负数会不一样,但是也可以看到,这种方法必须循环32次。

优化1:

        让u_i的每一位都与1进行按位与,结果为1的话就让cut++,然后将u_i右移一位之后循环,结束条件是u_i的二进制位都为0即u_i为0或者右移32次之后结束循环。

#include <stdio.h>
int main () {
    int i;
    size_t u_i;
    int cut = 0;
    scanf("%d", &i);
    u_i = (size_t)i; // 将负数强制转换为无符号整数
    while (u_i && cut < 32) {  // 判断是否溢出(32机器为例)
        if (u_i & 1) {
            cut++;
        }
        u_i >>= 1;
    }
    printf("%d\n", cut);
    return 0;
}

优化2: 与n-1按位求与

循环的让num = num & (num-1),直到num为0时循环结束

 

 这里每一次循环的效果是去掉最低位的一个1,所以这里的循环次数与二进制中1的个数相同。

int main() {
    int i;
    int cut = 0;
    scanf("%d", &i);
    int sum=0;
    while (i) {
        sum++;
        cut++;
        i = i & (i-1);
    }
    printf("cut=%d, sum=%d\n", cut,sum);
    return 0;
}

02-求二进制中不同位的个数

求两个数二进制中不同位的个数:牛客网链接

        只需要让这两个数按位异或,那么异或得到的这个数的二进制中1的个数就是两个数二进制中不同位的个数;然后再用移位运算02三种方法中的其中一种求出1的个数就可以了。

#include "stdio.h"
 
int main() {
    int m,n;
    scanf("%d %d", &m,&n);
    int num = m ^ n;
    int cut=0;
    while (num) {
        cut++;
        num = num & (num -1);
    }
    printf("cut=%d\n",cut);
    return 0;
}

03-打印整数的奇偶二进制位

编写代码实现:获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列。

思路分析:对于打印一个整数的二进制位,可以也使用移位操作符和位操作符,然后配合循环来实现;但是这里需要注意的是:屏幕上先打印的应该是二进制中的高位,这样才符合我们阅读数字的习惯,所以循环变量的初始值要设置为高。

int main() {
    int m;
    scanf("%d", &m);
    for (int i = 31; i >=1 ; i-=2) { // 奇
        printf("%d ", (m >> i) & 1);
    }
    printf("\n");
    for (int i = 30; i >= 0; i-=2) { // 偶
        printf("%d ", (m >> i) & 1);
    }
    printf("\n");
    
    return 0;
}
   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

1planet

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值