常见位运算的详讲!

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

常见位运算的详讲!_第1张图片


位运算的介绍!

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!

常见位运算的详讲!_第2张图片

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

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


2.将第x位修改为1!

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

常见位运算的详讲!_第3张图片

n|=(1<


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

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

常见位运算的详讲!_第4张图片 此时,只需要再次进行按位与操作即可将n的第x为修改为0!

n&=(~(1<


3.位图的思想

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


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

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

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

常见位运算的详讲!_第5张图片

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

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

 n&(-n)!


5.干掉最右侧的1!

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

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

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


位运算常见的经典例题!

 1.只出现一次的数字

136. 只出现一次的数字icon-default.png?t=N7T8https://leetcode.cn/problems/single-number/常见位运算的详讲!_第6张图片

代码如下: 

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

        int ret=0;
        for(int i=0;i

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


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

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

常见位运算的详讲!_第7张图片

代码如下:

class Solution
{
public:
    int singleNumber(vector& 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次,故不能通过简单的异或进行求解!

那么该如何求解呢?我们可以通过每个数字相应的比特位之和来求出结果!图示如下:常见位运算的详讲!_第8张图片

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


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

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

常见位运算的详讲!_第9张图片 代码如下:

class Solution {
public:
    vector singleNumber(vector& nums)
    {
        int i=0;
        int ret=0;
        for(;i>i)&1)==1)
            {
                pos=i;
                break;
            }
        }
        vectorr(2);
        for(i=0;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/

 

常见位运算的详讲!_第10张图片 代码如下:

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>ch)&1)==1)
            {
                return false;
            }
            ret|=1<

算法思路:

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

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


5.两整数之和

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

常见位运算的详讲!_第11张图片

代码:

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

 思路:

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

常见位运算的详讲!_第12张图片

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


5.消失的两个数字

面试题 17.19. 消失的两个数字icon-default.png?t=N7T8https://leetcode.cn/problems/missing-two-lcci/常见位运算的详讲!_第13张图片

代码:

class Solution {
public:
    vector missingTwo(vector& 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直接表示为那个数字!

        //再根据异或的性质,将数组中的元素分为两大阵营,然后每次异或即可得到缺失的两个数字!
        vectorv(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即可!


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

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

常见位运算的详讲!_第14张图片

你可能感兴趣的:(算法,c++,蓝桥杯,学习方法)