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

Posted 英雄哪里出来

tags:

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

零、写在前面

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

一、概念定义

1、位或的定义

  位或运算符是一个二元的位运算符,也就是有两个操作数,表示为x | y
  位或运算会对操作数的每一位按照如下表格进行运算,对于每一位只有 0 或 1 两种情况,所以组合出来总共 2 2 = 4 2^2 = 4 22=4 种情况。

左操作数右操作数结果
000
011
101
111

  通过这个表,我们得出一些结论:
  1)无论是 0 或 1,只要位或上 1,就变成1;
  2)只有当两个操作数都是0的时候,才变成 0;

#include <stdio.h>
int main() 
    int a = 0b1010;           // (1)
    int b = 0b0110;           // (2)
    printf("%d\\n", (a | b) ); // (3)
    return 0;

  • ( 1 ) (1) (1) 在C语言中,以0b作为前缀,表示这是一个二进制数。那么a的实际值就是 ( 1010 ) 2 (1010)_2 (1010)2
  • ( 2 ) (2) (2) 同样的,b的实际值就是 ( 0110 ) 2 (0110)_2 (0110)2
  • ( 3 ) (3) (3) 那么这里a | b就是对 ( 1010 ) 2 (1010)_2 (1010)2 ( 0110 ) 2 (0110)_2 (0110)2 的每一位做表格中的|运算。

  所以最后输出结果为:14
  因为输出的是十进制数,它的二进制表示为: ( 1110 ) 2 (1110)_2 (1110)2

2、位或运算符的简单应用

1)设置标记位

【例题1】给定一个数,判断它二进制低位的第 5 位,如果为 0,则将它置为 1。

  这个问题,我们很容易联想到位或。
  我们分析一下题目意思,如果第 5 位为 1,不用进行任何操作;如果第 5 位为 0,则置为 1。言下之意,无论第五位是什么,我们都直接置为 1即可,代码如下:

#include <stdio.h>
int main() 
    int x;
    scanf("%d", &x);
    printf("%d\\n", x | 0b10000); 
    return 0;

2)置空标记位

【例题2】给定一个数,判断它二进制低位的第 5 位,如果为 1,则将它置为 0。

  这个问题,我们在学过 《算法零基础100讲》(第42讲) 位运算 (位与) 入门 以后,很容易得出这样一种做法:

#include <stdio.h>
int main() 
    int x;
    scanf("%d", &x);
    printf("%d\\n", x & 0b11111111111111111111111111101111); 
    return 0;

  其它位不能变,所以位与上1;第5位要置零,所以位与上0;
  这样写有个问题,就是这串数字太长了,一点都不美观,而且容易写错,当然我们也可以转换成 十六进制,转换的过程也有可能出错。
  而我们利用位或,只能将第5位设置成1,怎么把它设置成0呢?

我们可以配合减法来用。分成以下两步:
  1)首先,强行将低位的第5位置成1;
  2)然后,强行将低位的第5位去掉;

  第 ( 1 ) (1) (1) 步可以采用位或运算,而第 ( 2 ) (2) (2) 步,我们可以直接用减法即可。代码实现如下:

#include <stdio.h>
int main() 
    int x;
    int a = 0b10000; 
    scanf("%d", &x);
    printf("%d\\n", (x | a) - a ); 
    return 0;

  注意:直接减是不行的,因为我们首先要保证那一位为 1,否则贸然减会产生借位,和题意不符。

二、题目描述

给定一个整数 x x x,将它低位连续的 0 都变成 1。

三、算法详解

  假设这个整数低位连续有 k k k 个零,二进制表示如下: . . . 1 00...00 ⏟ k ...1\\underbrace00...00_\\rm k ...1k 00...00   那么,如果我们对它进行减一操作,得到的二进制数就是: . . . 0 11...11 ⏟ k ...0\\underbrace11...11_\\rm k ...0k 11...11 我们发现,只要对这两个数进行位或,就能得到: . . . 1 11...11 ⏟ k ...1\\underbrace11...11_\\rm k ...1k 11...11 也正是题目所求。

四、源码剖析

int change(int x) 
	return x | (x-1);

五、推荐专栏

🌌《算法零基础100讲》🌌

(第1讲) 幂和对数

六、习题练习

序号题目链接难度
1根据数字二进制下 1 的数目排序★☆☆☆☆
2 二进制表示中质数个计算置位★☆☆☆☆
32 的幂★☆☆☆☆
4统计按位或能得到最大值的子集数目★★★☆☆

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

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

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

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

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

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

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