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

Posted 英雄哪里出来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《算法零基础100讲》(第49讲) 位运算 (右移)相关的知识,希望对你有一定的参考价值。

零、写在前面

  这是《算法零基础100讲》 专栏打卡学习的第四十九天了。
  每天打卡的题,做不出来没关系,因为困难的题涉及知识点较多,后面还是会开放出来的,就像昨天的 最大公约数 那道题今天还是会有,所以不要着急,内容能看懂,能自己分析,能做出简单题,就可以打卡。
  在刷题的过程中,总结自己遇到的坑点,写出 「 解题报告 」 供他人学习,也是一种自我学习的方式。这就是经典的帮助他人的同时,成就自己。目前, 「 万人千题 」 社区 每天都会有五六篇高质量的 「 解题报告 」 被我 「 加精 」。如果觉得自己有能力的,也可以来发布你的 「 解题报告 」。千万级流量,你我共同拥有。

一、概念定义

1、右移运算符的定义

1)右移的二进制形态

  右移运算符是一个二元的位运算符,也就是有两个操作数,表示为x >> y。其中xy均为整数。
  x >> y念作:“将 x x x 右移 y y y 位”,这里的位当然就是二进制位了,那么它表示的意思也就是:先将 x x x 用二进制表示,对于正数,右移 y y y 位;对于负数,右移 y y y 位后高位都补上 1。
  举个例子:对于二进制数 8 7 10 = ( 1010111 ) 2 87_10 = (1010111)_2 8710=(1010111)2 左移 y y y 位的结果就是:
( 1010 ) 2 (1010)_2 (1010)2

2)右移的执行结果

  x >> y的执行结果等价于: ⌊ x 2 y ⌋ \\lfloor \\frac x 2^y \\rfloor 2yx其中 ⌊ a ⌋ \\lfloor a\\rfloor a 代表对 a a a 取下整。如下代码:

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

  输出结果为:10
  正好符合这个右移运算符的实际含义: 10 = ⌊ 87 2 3 ⌋ 10 = \\lfloor \\frac 87 2^3 \\rfloor 10=2387

由于除法可能造成不能整除,所以才会有 取下整 这一步运算。

3)负数右移的执行结果

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

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

  它的输出为:-1
  我们发现同样是满足 ⌊ x 2 y ⌋ \\lfloor \\frac x 2^y \\rfloor 2yx 的(注意,负数的 取下整 和 正数 是正好相反的),这个可以用补码来解释,-1的补码为: 11111111   11111111   11111111   11111111 11111111 \\ 11111111 \\ 11111111 \\ 11111111 11111111 11111111 11111111 11111111 右移一位后,由于是负数,高位补上 1,得到: 11111111   11111111   11111111   11111111 11111111 \\ 11111111 \\ 11111111 \\ 11111111 11111111 11111111 11111111 11111111 而这,正好是 -1的补码,同样,继续右移 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;

4)右移负数位是什么情况

  刚才我们讨论了 x < 0 x < 0 x<0 的情况,那么接下来,我们试下 y < 0 y < 0 y<0 的情况会是如何?
  是否同样满足: ⌊ x 2 y ⌋ \\lfloor \\frac x 2^y \\rfloor 2yx 呢?
  如果还是满足,那么两个整数的左移就有可能产生小数了。
  看个例子:

#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;

  虽然能够正常运行,但是结果好像不是我们期望的,而且会报警告如下:

[Warning] right shift count is negative [-Wshift-count-negative]

  实际上,编辑器告诉我们尽量不用右移的时候用负数,但是它的执行结果不能算错误,起码例子里面对了。右移负数位其实效果和左移对应正数数值位一致。
  左移相关的内容,可以参考:《算法零基础100讲》(第48讲) 位运算 (左移)

2、右移运算符的应用

1)去掉低 k 位

【例题2】给定一个数 x x x,去掉它的低 k k k 位以后进行输出。

  这个问题,可以直接通过右移来完成,如下:x >> k

2)取低位连续 1

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

  对于一个数 x x x,假设低位有连续 k k k 个 1。如下: ( . . . 0 1...1 ⏟ k ) 2 (...0\\underbrace1...1_\\rm k)_2 (...0k 1...1)2 然后我们将它加上 1 以后,得到的就是: ( . . . 1 0...0 ⏟ k ) 2 (...1\\underbrace0...0_\\rm k)_2 (...1k 0...0)2 这时候将这两个数异或结果为: ( 1...1 ⏟ k + 1 ) 2 (\\underbrace1...1_\\rm k+1)_2 (k+1 1...1)2
  这时候,再进行右移一位,就得到了 连续 k k k 个 1 的值,也正是我们所求。所以可以用以下语句来求:(x ^ (x + 1)) >> 1

3)取第k位的值

【例题4】获取一个数 x x x 的第 k ( 0 ≤ k ≤ 30 ) k(0 \\le k \\le 30) k(0k30) 位的值并且输出。

  对于二进制数来说,第 k k k 位的值一定是 0 或者 1。
  而 对于 1 到 k − 1 k-1 k1 位的数字,对于我们来说是没有意义的,我们可以用右移来去掉,再用位与运算符来获取二进制的最后一位是 0 还是 1,如下:(x >> k) & 1

二、题目描述

  给你一个整数 n n n,请你判断该整数是否是 2 2 2 的幂次方。如果是,返回 true;否则,返回 false。如果存在一个整数 x x x 使得 n = = 2 x n == 2^x n==2x,则认为 n n n 2 2 2 的幂次方。

三、算法详解

  这道题在这个专栏里已经出现了无数次了,因为它确实可以 “一题多做”。这次的用法,就是利用迭代的方式,不断将数右移一位,如果得到的结果为 1,就是 2 的幂;如果不为一,但是是奇数,则表示一定不是 2 的幂。

四、源码剖析

bool isPowerOfTwo(int n)
    int i;
    if(n <= 0) 
        return false;
    
    while(1) 
        if(n == 1) 
            return true;     // (1)
        
        if(n & 1) 
            return false;    // (2)
        
        n >>= 1;             // (3)
    
    return false;

  • ( 1 ) (1) (1) 代表最高位为 1,其它低位都是 0;
  • ( 2 ) (2) (2) 代表 1 出现在非最高位的位置上;
  • ( 3 ) (3) (3) 执行一次右移;

五、推荐专栏

💜《夜深人静写算法》💜
三)初等数论入门

六、习题练习

序号题目链接难度
1位1的个数★☆☆☆☆
22的幂★☆☆☆☆
34的幂★☆☆☆☆
4数字转换为十六进制数★★☆☆☆
👇🏻 添加 博主 参加九日集训👇🏻

以上是关于《算法零基础100讲》(第49讲) 位运算 (右移)的主要内容,如果未能解决你的问题,请参考以下文章

《算法零基础100讲》(第48讲) 位运算 (左移)

《算法零基础100讲》(第44讲) 位运算 (位或) 入门

《算法零基础100讲》(第50讲) 位运算 (按位取反)

《算法零基础100讲》(第45讲) 位运算 (位或) 进阶

《算法零基础100讲》(第47讲) 位运算 (异或) 进阶

《算法零基础100讲》(第13讲) 最大公约数