【题解】《算法零基础100讲》(第49讲) 位运算 (右移)

一. 概念定义

1.1 右移运算符的定义

  右移运算符是一个二元的位运算符,表示为 x >> y,其中x和y均为整数。
   x >> y念作:将x向右位移y位。表示的意思是将x的二进制向右位移y位,对于正数头部用0补上,对于负数,头部用1补上。

例如:

 (1010)₂ >> 2
=>(0010)₂
结果就是10>>2 = 2

1.2 右移的执行结果

  右移的执行结果等价于(x >> y)

「x / 2 ^ y」
上面的框框是向下取整的意思。

代码如下:

#include <stdio.h>
int main() {
    int x = 0b1010;
    int y = 2;
    printf("%d\n", x >> y);
    return 0;
}

结果为:2

1.3 负数右移的执行结果

  所谓负数右移,就是在 x >> y 中,x为负数的情况。
代码如下:

#include <stdio.h>
int main() {
    printf("%d\n", -1 >> 1);
    return 0;
}

输出结果为:-1
它也符合「x / 2 ^ y」的执行结果(注意负数的向下取整与正数相反),这个可以用补码来解释:
-1的补码

1111 1111 1111 1111 1111 1111 1111 1111

右移一位后,首位补1:

1111 1111 1111 1111 1111 1111 1111 1111

刚好与-1的补码相等。所以我们可以认为-(x >> y) 与 -x >> y是等价的

例题1:要求不运行代码,分析出这段代码的输出结果为什么

#include <stdio.h>
int main() {
    int x = (1 << 31) | (1 << 30) | 1;
    int y = (1 << 31) | (1 << 30) | (1 << 29);
    printf("%d\n", (x >> 1) / y);
    return 0;
}

1.4 右移负数位是什么情况

  刚刚我们讨论了x < 0的情况,那么现在我们考虑一下 y < 0的情况是如何的?
  是否还满足:「x / 2 ^ y」

看如下例子:

#include <stdio.h>
int main() {
    printf("%d\n", 1 >> -1);   // 2
    printf("%d\n", 1 >> -2);   // 4
    printf("%d\n", 1 >> -3);   // 8
    printf("%d\n", 1 >> -4);   // 16
    printf("%d\n", 1 >> -5);   // 32
    printf("%d\n", 1 >> -6);   // 64
    printf("%d\n", 1 >> -7);   // 128
    return 0;
}

这段代码在Dev上是可以运行的,但是会出现如下警告
在这里插入图片描述
编译器告诉我们尽量不要用右移负数,右移负数和左移对应的整数的效果是一样得

1.5 右移运算符的应用

1.5.1 去掉低k位

例题2:给定一个数x,去掉他的低k位后输出。

我们直接输出x >> k即可。

1.5.2 取低位连续一

例题3:获取一个数x低位连续的1并且输出。

对于一个数x,假设它的低位由连续k个1,

(...011....1)₂

则对其进行加1
得:

(…100…0)₂

而这两个数的异或结果为:

(11....1)₂
k+1个1

这时候右移一位则得到连续k个1的值,计算语句:(x ^ (x + 1)) >> 1;

1.5.3 取低k的值

例题4:取一个数x的低k位的值(0 <= k <= 30)

我们先将x向右移动k位,将低k位移动到最尾部,然后再与1进行按位与计算即可
计算公式:(x >> y) & 1

二. 推荐专栏

《算法零基础100讲》(第49讲) 位运算 (右移)

三. 相关练习

3.1 位1的个数

191. 位1的个数

思路分析:

这道题我们需要应用一个公式n & (n - 1),其计算结果就是,将最低位的1去除,所以我们每去除以此,就++一次,直到n为0。

代码如下:

int hammingWeight(uint32_t n) {
    int count = 0;
    while(n){
        n = n & (n - 1);//消去当前最低为的1
        count ++;
    }
    return count;
}

3.2 2的幂

231. 2 的幂

判断是不是2的幂我们只需对其进行按位与计算, n & (n - 1), 如果结果位0, 则为2的幂,否则不是

代码如下:

bool isPowerOfTwo(int n){
    if(n <=0 ) return false;
    return !(n &(n-1));
}

3.3 4的幂

342. 4的幂

方法一:

还是用试除法

bool isPowerOfFour(int n){
    if(n == 1){
        return true;
    }
    long long int t = 1;
    while(t != n){
        t *= 4;
        if(t > n){
            return false;
        }
    }
    return true;
}

方法二:

我么可以发现4的所有幂的二级制形式中偶数位为1,奇数位为0,所以我们可以判断其奇数位是存在1,如果存在,则不是

代码如下:

bool isPowerOfFour(int n){
    return n > 0 && (n & (n - 1)) == 0 && (n & 0xaaaaaaaa) == 0;
}

时间复杂度为O(1)

3.4 数字转换为十六进制数

405. 数字转换为十六进制数

位移法:

我们通过位移法对其进行计算,我们已知,一个十六进制位对应四个二进制位,所以我们对二进制进行4个4个的转化,我们通过(num>>i) & 0xf的方式将每四位二进制数分别取出,并存放在val中,然后再判断val是否小于10,如果小于10,则将对应的09计入在数组中,否则将af计入。最后我们还要将数组反转,因为我们是从尾部开始的。

代码如下:

void resever(char* s, int len){
    int i = 0;
    int j = len;
    while(i < j){
        char tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
        i++;j--;
    }
}

char * toHex(int num){
    if(num == 0){
        return "0";
    }
    char* ans = (char*)malloc(sizeof(char) * 10);
    int ansSize = 0;
    for(int i = 0; i < 32; i += 4){
        int val = (num >> i) & 0xf;
        char c = val < 10 ? val + '0' : (val - 10)+'a'; 
        //printf("%d ", val & 0xf < 10);
        if(val || (num >> i)){//判断num是否为0
            ans[ansSize++] = c;
        }
    }
    ans[ansSize] = '\0';
    resever(ans, ansSize - 1);
    return ans;
}
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

友人苏

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

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

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

打赏作者

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

抵扣说明:

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

余额充值