常见位运算的详讲!

news2025/2/23 14:08:35

今日为大家详细讲解一番关于常见位运算的操作,本文主要介绍一些位运算的操作符,然后再通过简单->中等->困难的例题,让大家彻底搞懂关于位运算的知识!


位运算的介绍!

1.基础位运算

">>"右移操作符!

"<<"左移操作符!

"~" 按位取反操作符!

"&"按位与操作符!

"|" 按位或操作符!

"^"异或操作符!


 上面是一些常见的位运算操作符!下面来一一介绍每个运算符的作用!

1.“>>”右移操作符!

右移操作符分为两种,一种为逻辑移位,一种是算术移位! 二者的移位规则如下:

1. 逻辑移位:左边用0填充,右边丢弃

2. 算术移位:左边用原该值的符号位填充,右边丢弃


 2."<<"左移操作符! 

移位规则:左边抛弃,右边补0。

注:其中左移操作和右移操作还有另外一个中作用,左移的另一作用就是将原来的数字扩大两倍!右移操作将原来的数字缩小两倍!


3.按位取反

将原二进制数字的二进制位进行按位取反!包括符号位也要进行取反!


4.按位与

只有两个数字对应的比特位都为1,结果才为1!

注:有0则0!


5.按位或

两个数字对应的比特位存在1,结果就为1!

注:有1则1!


6.按位异或(也叫无进位相加)后面例题会根据此特性求解!

只有两个数字的二进制位不同的时候才为1!相同为0!

注:相同为0,相异为1!


介绍了上面的简单的概念之后,下面进入关于这些操作符的扩充了解!

位操作符的相关运算扩充!

1.确定一个数的二进制位为0或为1!

给定一个数字,确定其二进制位中第x为是0还是1!

其中要想判断第x位是0还是1!只需要将N这个数字,向右移动x位!使得该位移动到最低位!

再根据按位与操作即可判断改为是0还是1!


2.将第x位修改为1!

要想将第x位修改为1! 只需要先将1向左移动x位,让其与原数字的第x位对齐!然后进行或操作!即可将第x为修改为1! 图示如下:

n|=(1<<x);


3.将一个数字n的第x为修改为0!

原理类似于将第x为修改为1!只需要先将1向左移动x位!然后进行取反!因为不能将其他位进行修改,所以此时1的第x为0,其余位都为1,然后再进行按位与操作即可修改成功!

 此时,只需要再次进行按位与操作即可将n的第x为修改为0!

n&=(~(1<<x));


3.位图的思想

位图的思想类似于哈希表!因为一个整形4byte,32个比特位!所以可以根据比特位来进行某些题目的求解!一会通过一些例题来体现位图的思想!


4.提取一个数最右边的1!

先求出此数的相反数!然后进行按位与操作即可!

求一个数的相反数的二进制时,可以发现一个规律,找到其最右侧的1,其左边区域都相反,右边区域不变!

可以看出,一个数的相反数,其二进制位中的数字,在第一次出现1的位置时,分为了两部分,左边全部与之前相反,右边不变!

所以根据此特性,再进行按位与操作即可以获得最右边的一个1!

 n&(-n)!


5.干掉最右侧的1!

其实本质上就是进行了借位操作!因为借位都是先从低位进行操作!所以进行借位之后即可干掉最右侧的1!

n-1之后,其一定会减少一个1!然后进行按位与操作即可将最后一个1干掉!根据此特性还可以快速求出二进制数中1的个数!

后面会通过例题来体现此特征!


位运算常见的经典例题!

 1.只出现一次的数字

136. 只出现一次的数字icon-default.png?t=N7T8https://leetcode.cn/problems/single-number/

代码如下: 

class Solution 
{
public:
    int singleNumber(vector<int>& nums) 
    {
        //位操作!

        int ret=0;
        for(int i=0;i<nums.size();i++)
        {
            ret^=nums[i];
        }
        return ret;
    }
}

本题直接通过异或操作直接秒杀!异或操作相同为0相异为1,因为只有一个数字出现一次而其他数字都出现两次,所以可以直接通过异或解决此题


2.只出现一次的数字||

LCR 004. 只出现一次的数字 IIicon-default.png?t=N7T8https://leetcode.cn/problems/WGki4K/

代码如下:

class Solution
{
public:
    int singleNumber(vector<int>& nums)
    {
        int ret = 0;
        for(int i = 0; i < 32; i++) // 依次去修改 ret 中的每⼀位
        {
            int sum = 0;
            for(int x : nums)
            {
                if(((x >> i) & 1) == 1)
                {
                         sum++;
                }
                   
                        sum %= 3;
            } // 计算nums中所有的数的第 i 位的和
                
                if(sum == 1) ret |= 1 << i;
        }
        return ret;
    }

};

 此题因为数组中只有一个元素出现1此,其余都出现了3次,故不能通过简单的异或进行求解!

那么该如何求解呢?我们可以通过每个数字相应的比特位之和来求出结果!图示如下:

可以看出,我们只需要将每一个比特位的和求出来然后再%3即可判断出出现一次的数字! 因为由上图我们可以看到,出现一次的数字和%3之后的结果相同,所以我们通过此特性可以巧妙求出出现一次的数字的各个比特位,依次求出比特位,最后即可得到只出现一次的数字! 


3.只出现一次的数字|||

260. 只出现一次的数字 IIIicon-default.png?t=N7T8https://leetcode.cn/problems/single-number-iii/

 代码如下:

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums)
    {
        int i=0;
        int ret=0;
        for(;i<nums.size();i++)
        {
            ret^=nums[i];
        }

        //得到a^b的结果!!
        //然后求出a^b的第一个一的位数!
        int pos=0;
        for(i=0;i<32;i++)
        {
            if(((ret>>i)&1)==1)
            {
                pos=i;
                break;
            }
        }
        vector<int>r(2);
        for(i=0;i<nums.size();i++)
        {
            if(nums[i]>>pos&1==1)
            {
                r[0]^=nums[i];
            }
            else
            {
                r[1]^=nums[i];
            }
        }
        return r;
    }
};

通过此题,我们可以看出有两个出现一次的数字!所以我们将所以元素异或之后得到的结果是只出现一次的数字a,b的异或结果!那么如何将a,b分离出来呢?

我们知道!a^b一定有不相同的比特位! 那么我们就可以找到第一次出现1的比特位(div)!然后根据此特性,将a,b分离开来,由此再次遍历一遍数组,将数组分为两大阵营!以a为首和以b为首的两大阵营!根据与div按位与不同的结果就此分开,因为其余数组都出现两次,所以划分阵营,相同的数字肯定在同一阵营,在同一阵营,异或之后结果为0,最后即可求出a,b!


 4.判断字符是否唯一

面试题 01.01. 判定字符是否唯一icon-default.png?t=N7T8https://leetcode.cn/problems/is-unique-lcci/

 

 代码如下:

class Solution {
public:
    bool isUnique(string astr)
    {
        //根据鸽巢原理直接排除字符个数大于26的情况!


        int len=astr.size();
        if(len>26)
        {
            return false;
        }

        //采用位图的思想直接进行求解!每个比特位对应相应的字母!映射到int类型中!
        //如果进入前个数为1的话,说明一定重复!直接返回false即可!否则一直遍历,直至字符串遍历完!!
        int ret=0;
        for(int i=0;i<len;i++)
        {
            int ch=astr[i]-'a';

            if(((ret>>ch)&1)==1)
            {
                return false;
            }
            ret|=1<<ch;
        }
        return true;
    }
};

算法思路:

此题我们可以利用哈希表进行统计数组出现的次数!但是我们可以根据位图的思想使空间复杂度降为o(1),定义一个整形变量,32个比特位,足够我们统计进行映射!每次进行统计!若统计前为0,则将此次统计的数字的比特位修改为1,根据按位或的特征将此比特位修改为1!若进入前为1,那么一定会重复!直接返回即可!否则一直判断,直至string遍历完毕!

注:还可以根据鸽巢原理(鸽巢原理又名狄利克雷抽屉原理、鸽笼原理。 10只鸽子放进9个鸽笼,那么一定有一个鸽笼放进了至少两只鸽子)进行优化!


5.两整数之和

371. 两整数之和icon-default.png?t=N7T8https://leetcode.cn/problems/sum-of-two-integers/

代码:

class Solution {
public:
    int getSum(int a, int b)
    {
        

        //根据异或的无进位相加特性进行求解!
        //再根据按位与的操作求出合适的进位处!!
        int sum=0;
        int ad=0;
        while(b)
        {
            sum=a^b;
            ad=(a&b)<<1;

            a=sum;
            b=ad;
        }
        return a;
    }
};

 思路:

题目明确指出不能使用‘+’,‘-’,这就大大限制了我们,但是我们前面介绍到了异或操作符!前面又说道异或操作符为无进位相加!可以根据此特性来进行求解!

又因为我们相加的结果没有进位!所以那么如何该得到进位呢? 我们知道只有两个数字对应的比特位都为1才能有进位的存在!而且进位进的是其前面那一位! 所以我们将a,b进行按位与操作!然后向左移一位,获得进位!然后与异或结果继续异或!直至进位为0,结束运算得到最后结果!


5.消失的两个数字

面试题 17.19. 消失的两个数字icon-default.png?t=N7T8https://leetcode.cn/problems/missing-two-lcci/

代码:

class Solution {
public:
    vector<int> missingTwo(vector<int>& nums)
    {
        //首先根据缺失的数字可以求出缺失两数字异或的结果!
        //在根据消失的数字||思路即可求解!
        int ret=0;
        //得出缺失两数字的异或和!
        for(auto ch:nums)
        {
            ret^=ch;
        }
        for(int i=1;i<=nums.size()+2;i++)
        {
            ret^=i;
        }

        //找出异或结果的第一个1的位置!

        int div=0;
        div=ret&(-ret);

        //其中div就是第一次出现的1的位置!  该div直接表示为那个数字!

        //再根据异或的性质,将数组中的元素分为两大阵营,然后每次异或即可得到缺失的两个数字!
        vector<int>v(2);
        for(auto ch:nums)
        {
            if((ch&div)==div)
            {
                v[0]^=ch;
            }
            else
            {
                v[1]^=ch;
            }
        }
        for(int i=1;i<=nums.size()+2;i++)
        {
            //对应的比特位相同!

            if((i&div)==div)
            {
                v[0]^=i;
            }
            else
            {
                v[1]^=i;
            }
        }
        return v;
    }
};

 思路:

因为题目是从1~n缺少两个数字,我们可以容易的求出缺少两数字异或的结果!然后再根据只出现一次的数字|||本题的思路即可求解!

此题就是只出现一次的数字|||的思路加上一个两数异或的结果的结合!虽然难度为困难,但是实际上手还是很容易的!本题思路不再累赘,参考3即可!


至此,关于运算符运算的知识讲解结束,希望读完本篇文章,可以让读者对运算符的运算有了更深一步的认识,希望读者读完文章可以自行尝试一下上面的例题!对我们的位运算的性质掌握也会有很大的提升!

本文分享完毕,欢迎读者们多多评论,若有所收获,留下免费的小心心哦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1253337.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c语言练习12周(6~10)

以下程序调用递归函数fun实现求n!&#xff0c;请补充代码。 题干以下程序调用递归函数fun实现求n!&#xff0c;请补充代码。 int fun(int n) { int c; /****************/ /****************/ else cn*fun(n-1); …

1.3 取反器和8位取反器

取反器真值表: 取反开关输入输出011000110101 取反器相当于一个异或门 8位取反器

Canvas艺术之旅:探索锚点抠图的无限可能

说在前面 在日常的图片处理中&#xff0c;我们经常会遇到需要抠图的情况&#xff0c;无论是为了美化照片、制作海报&#xff0c;还是进行图片合成。抠图对于我们来说也是一种很常用的功能了&#xff0c;今天就让我们一起来看下怎么使用canvas来实现一个锚点抠图功能。 效果展示…

从0到0.01入门 Webpack| 005.精选 Webpack面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

【c++】——类和对象(下) ——内存管理

作者:chlorine 专栏:c专栏 目录 &#x1f4bb; C/C内存分布 &#x1f4bb;C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free ​编辑 &#x1f4bb;C内存管理方式 &#x1f449;new/delete操作内置类型 &#x1f449;new和delete操作自定义类型 &#x1f…

巧妙解决接口测试产生脏数据问题

测试数据创建后需要对其删除&#xff0c;不然可能产生脏数据&#xff0c;对开发和测试、生产环境造成一定影响。 其接口框架是基于Python&#xff0c;API规范基于REST。 产生原因 改进前&#xff1a;清除资源的操作放在每个正向测试用例里&#xff0c;没有在setUp和tearDown…

战地5无限序章(无法保存)的解决办法

启动游戏后&#xff0c;目录就会自动变成这样了&#xff0c;也不会无限循环了&#xff01;

QT自定义控件: QLineEdit设置密码可见性选择的三种方式(小眼睛样式)

一、重写QLineEdit提升控件(最好用) 代码中创建了PasswordLineEdit的自定义类,该类继承自QLineEdit。这个自定义类添加了一个QPushButton,用于切换密码的可见性。 实现步骤: 在构造函数中,创建了一个 QPushButton 对象 toggleButton 作为切换密码可见性的按钮,并设置了…

柑橘病害数据集(四类图像分类,没有打yolo标签)

1.文件夹分为训练集和测试集 在这个数据集中&#xff0c;有一类是新鲜柑橘&#xff0c;还有另外三种疾病&#xff0c;溃疡病、黑斑病和绿化病。 2.train文件夹 2.1.blackspot&#xff08;黑斑病&#xff09; 文件夹 206张照片 2.2.canker&#xff08;溃疡病&#xff09; 文…

cesium轨迹线(图片轨迹线)

cesium轨迹线(图片轨迹线) 下面有源码 实现思路 使用ellipse方法加载圆型,修改polyline中‘material’方法重写glsl来实现当前效果(cesium版本1.109) 示例代码 index.html <!DOCTYPE html> <html lang="en"><head

详解重排重绘

详解重排重绘 前言页面渲染页面渲染 重排重绘重排重绘哪个更耗时间避免重排重绘发生重排重绘实例 前言 本文主要讲解在页面加载成功后可能因为页面中DOM元素样式或布局被修改从而引发的重排重绘进行一个讲解&#xff0c;那么好&#xff0c;本文正式开始. 页面渲染 因为重排重…

类与对象——(1)初识对象——C++中的string

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 或许不安或许迷惑&#xff0c;但…

【 一篇通】H5 Canvas

文章目录 Canvas的创建(HTMLCanvasElement)图形绘制&#xff1a;H5为Canvas对应的2D上下文Context提供了一系列的画图接口保存save、恢复restore、变换Transformations Canvas的创建(HTMLCanvasElement) 定义canvas HTML元素&#xff0c;默认长宽300x150 <canvas width&qu…

BER编码规则

文章目录 一、BER 编码规则介绍二、BER编码数据组成三、Identifier octets Type1. tag class 分类2. p/c 分类3 tag type4. ASN.1的原始数据类型&#xff08;TAG&#xff09; 四、 Length octets1、定长的短格式2、不定长格式3、保留格式 五、Contents octets六 、BER编码番外 …

20231125硬盘电源线5线不能识别日立10T的硬盘的解决方法

20231125硬盘电源线5线不能识别日立10T的硬盘的解决方法 2023/11/25 23:00 缘起&#xff0c;在拼多多买了2片10TB的7200rpm的日立二手硬盘。 型号&#xff1a;日立 mar-2018 10T硬盘 接上电脑&#xff0c;硬盘感觉在转动了【正常上电了。】 但是X99主板&#xff0c;在WIN10下就…

leetcode设计循环队列(链表方式来实现)

上次我们那个设计循环队列的时候用的是数组&#xff0c;因为那个时候还是不太会链表&#xff0c;现在有了链表的思路&#xff0c;我们一起来看看解题步骤吧。 https://leetcode.cn/problems/design-circular-queue/description/ 设计循环队列 那我们其实最主要的就是我们这个…

时间序列预测实战(十九)魔改Informer模型进行滚动长期预测(科研版本)

论文地址->Informer论文地址PDF点击即可阅读 代码地址-> 论文官方代码地址点击即可跳转下载GIthub链接 个人魔改版本地址-> 文章末尾 一、本文介绍 在之前的文章中我们已经讲过Informer模型了&#xff0c;但是呢官方的预测功能开发的很简陋只能设定固定长度去预测未…

微软 Edge 浏览器目前无法支持 avif 格式

avif 格式在微软 Edge 浏览器中还是没有办法支持。 如果你希望能够查看 avif 格式&#xff0c;那么只能通过浏览器打开&#xff0c;然后浏览器将会把这个文件格式下载到本地。 avif 格式已经在其他的浏览器上得到了广泛的支持&#xff0c;目前不支持的可能就只有 Edge 浏览器。…

drool 7 multiThread 测试

基本信息 通过option &#xff0c;使用如下代码进行设置 //线程数量10MaxThreadsOption optionMaxThreadsOption.get(10);kieBaseConf.setOption(option);kieBaseConf.setOption(MultithreadEvaluationOption.YES);并发是以CompositeDefaultAgenda/Rule为颗粒度来的&#xff0…

Linux篇:文件管理

一、共识原理&#xff1a; 1. 文件内容属性&#xff0c;内容与属性都是数据&#xff0c;都要在磁盘中保存。 2. 文件分为打开的文件和没打开的文件。 3. 研究打开的文件&#xff1a;本质是研究进程和文件的关系&#xff0c;因为是进程负责打开文件。 4. 没打开的文件在存储介质…