常见位运算的详讲!

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


位运算的介绍!

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即可!


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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

纵观手机市场,手机即鏖战全面屏

9月13日&#xff0c;在相继发布Apple TV、Apple Watch 和iPhone 8/8 Plus之后&#xff0c;当大家都以为苹果新品发布会临近结束之时&#xff0c;苹果前CEO史蒂夫乔布斯的这句经典名言再现屏幕&#xff0c;iPhone X终于揭开了神秘面纱。 “One more thing”。 9月13日&#xff…

第一百七十九回 自定义SlideImageSwitch

文章目录 1. 概念介绍2. 思路与方法2.1 实现思路 3. 代码与效果3.1 示例代码3.2 运行效果 4. 内容总结 我们在上一章回中介绍了"SlideSwitch组件"相关的内容&#xff0c;本章回中将介绍自定义SlideImageSwitch.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概…

4、LED闪烁

LED亮灭 使用STC-ISP软件的延时计算器自动生成延迟子函数 #include <REGX52.H> #include <INTRINS.H>//延迟函数 void Delay500ms() //12.000MHz {unsigned char i, j, k;//_nop_()需要导入<INTRINS.H>包_nop_();i 4;j 205;k 187;do{do{while (--k);}…

【数据库篇】关系模式的表示——(2)规范化

范式&#xff1a;范式是符合某一种级别的关系模式的集合 规范化&#xff1a;是指一个低一级的范式的关系模式&#xff0c;通过模式的分解转换为若干个高一级范式的关系模式的集合。 1NF 每个分量必须是不可分开的数据项&#xff0c;满足这个条件的关系模式就是1NF。 2NF 若…

c语言判断三角形

以下是一个用C语言编写的程序&#xff0c;用于判断输入的三个数能否构成三角形。 #include <stdio.h>int main() { int a, b, c; printf("请输入三角形的三条边长&#xff1a;\n"); scanf("%d%d%d", &a, &b, &c); if (a b…

为什么淘宝取消双12活动?

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 淘宝取消双12活动了&#xff0c;这条消息犹如一颗重磅炸弹&#xff0c;在整个电商圈中引发了轩然大波。 不过呢&#xff0c;淘宝为了过度&#xff0c;把双12改了个名字叫“好价节”。估计是官方都…

isis基础大全学习案例

R1配置&#xff1a; isis 1 is-level level-2 //本区域只启用level-2级别 cost-style wide //默认为narrow窄度量&#xff0c;开销只能最大63&#xff0c;并且不能打tag&#xff0c;wide宽度量的tlv和narrow不匹配&#xff0c;不能相互计算路由&#xff0c;两边都要改。 netwo…

Kotlin学习——kt里的集合List,Set,Map List集合的各种方法之Int篇

Kotlin 是一门现代但已成熟的编程语言&#xff0c;旨在让开发人员更幸福快乐。 它简洁、安全、可与 Java 及其他语言互操作&#xff0c;并提供了多种方式在多个平台间复用代码&#xff0c;以实现高效编程。 https://play.kotlinlang.org/byExample/01_introduction/02_Functio…

自动化部署 扩容openGauss —— Ansible for openGauss

前言 大家好&#xff0c;今天我们为大家推荐一套基于Ansible开发的&#xff0c;自动化部署及扩容openGauss的脚本工具&#xff1a;Ansible for openGauss&#xff08;以下简称 AFO&#xff09;。 通过AFO&#xff0c;我们只需简单修改一些配置文件&#xff0c;即可快速部署多种…

JavaScript基础—运算符、表达式和语句、分支语句、循环语句、综合案例-ATM存取款机

版本说明 当前版本号[20231125]。 版本修改说明20231125初版 目录 文章目录 版本说明目录JavaScript 基础 - 第2天运算符算术运算符赋值运算符自增/自减运算符比较运算符逻辑运算符运算符优先级 语句表达式和语句分支语句if 分支语句if双分支语句if 多分支语句三元运算符&am…

人工智能时代的内容写作

内容不再只是王道&#xff0c;正如俗话所说&#xff1a;它是一种流动的货币&#xff0c;推动了巨大的在线信息和影响力经济。 每个品牌都是一个故事&#xff0c;通过其服务和商品讲述自己。尽管如此&#xff0c;大多数客户还是会通过您的在线内容最了解您。 但随着我们进入人…

鸿蒙HarmonyOS 编辑器 下载 安装

好 各位 之前的文章 注册并实名认证华为开发者账号 我们基实名注册了华为的开发者账号 我们可以访问官网 https://developer.harmonyos.com/cn/develop/deveco-studio 在这里 直接就有我们编辑器的下载按钮 我们直接点击立即下载 这里 我们根据自己的系统选择要下载的系统 例…

Linux操作系统之apt常用命令记录

文章目录 apt 命令apt 语法apt 常用命令列出所有可更新的软件清单命令升级软件包列出可更新的软件包及版本信息升级软件包&#xff0c;升级前先删除需要更新软件包安装指定的软件命令&#xff1a;安装多个软件包&#xff1a;更新指定的软件命令显示软件包具体信息,例如&#xf…

MYSQL基础之【创建数据表,删除数据表】

文章目录 前言MySQL 创建数据表通过命令提示符创建表使用PHP脚本创建数据表 MySQL 删除数据表在命令提示窗口中删除数据表使用PHP脚本删除数据表 后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;Mysql &#x1f431;‍&#x1f453;博主…

搜索 C. Tic-tac-toe

Problem - C - Codeforces 思路&#xff1a;搜索&#xff0c;判断合法性。从起始态用搜索进行模拟&#xff0c;这样可以避免后面判断合法性这一繁琐的步骤。用一个map进行映射当前态及对应的结果。剪枝&#xff1a;如果当前字符串已经被搜索过&#xff0c;则直接跳过去。 代码…

如何在3dMax中根据AutoCAD地形规划文件对地形进行建模?

在3dMax中根据Autocad地形规划文件对地形进行建模的方法 直入主题&#xff0c;要根据包含地形图的DWG (Autocad) 文件进行地形建模&#xff0c;方法步骤如下&#xff1a; 1.运行3dmax软件&#xff0c;点击“文件&#xff08;File&#xff09;->导入&#xff08;Import&…

【RTP】RTPSenderAudio::SendAudio

RTPSenderAudio 可以将一个opus帧封装为rtp包进行发送,以下是其过程:RTPSenderAudio::SendAudio :只需要提供payload部分 创建RtpPacketToSend 并写入各个部分 填充payload部分 sender 本身分配全session唯一的twcc序号 if (!rtp_sender_->

pandas根据列正逆序排序

题目&#xff1a;根据 buy_quantity 列进行排名&#xff0c;相同值分配相同的最低排名。 import pandas as pd# 创建一个示例 DataFrame data {item_id: [1, 2, 3, 4, 5, 6, 7], buy_quantity: [1, 2, 2, 3, 3, 4, 5]} df pd.DataFrame(data)# 使用 rank() 函数为 buy_quant…

NX二次开发UF_CURVE_ask_int_parms 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_ask_int_parms Defined in: uf_curve.h int UF_CURVE_ask_int_parms(tag_t int_curve_object, int * num_objects_set_1, tag_t * * object_set_1, int * num_objects_set_…

2. 寄存器

锁存器,用于存储1位的电路 只有当 可写位(write enable)开启,才会把输入写到输出,同时保存输出 使用锁存器 带时钟的锁存器 带时钟带可写控制的完整版锁存器 下面的时钟使用按钮来代替, 只有按钮为1时,相连的电路才工作时钟的作用在于协同所有电路共同工作,也是一切电路自动化…