位运算全解析:从入门到精通

常见的位运算

&按位与:两个位都为 1 时,结果为 1;否则为 0。记忆方法:有0则0,就是平时的逻辑运算的&&只有两个都为真的时候才能出1

|按位或:两个位中至少有一个为 1 时,结果为 1;否则为 0。记忆方法:有1则1,就是平时的逻辑运算的||,有一个真就出1

^按位异或:两个位不同时为 1,相同时为 0。记忆方法:相同为0,相异为1 / 无进位相加,这两种方法最好都记一下

异或异或就是相异为1

无进位相加 比如0^0  类比二进制的0+0=0所以结果为0

1^1 =1+1=10,但是不看进位不进位,看最低位即可,所以为0

1^0=1+0=1,所以结果为1

综上:

按位与:有0则0

按位或:有1则1

按位异或:相异为1 / 无进位相加(只看本位)

<<左移:将二进制位向左移动 n 位,高位丢弃,低位补 0。相当于*2,因为是二进制

比如3<<2,011<<2,1100=12,3*2*2=12

注意:对负数进行左移会出错

>>右移

无符号:逻辑右移,高位补 0

有符号:算术右移,高位补符号位(正数补 0,负数补 1

相当于除2,因为是二进制

比如8>>2,1000>>2,0010=2,8/2/2=2

~按位取反:将位 0 转为 11 转为 0(单目运算符)。

比如对~3  3的原反补码相同,00000011,然后按位取反,11111100(这是补码)减一变成反码

11111011,符号位不变,按位取反,10000100,此时就是-4   ~x=-(x+1)  ~3=-(3+1)=-4

注意:左移就是往高位移,右移就是往低位移

优先级:~     >> / <<     &    ^     |     (这里能加括号就尽量加括号,也方便阅读)

基础常见位运算题型

在这里我们做一下统一:全部从低位到高位,下标从0开始,所以对于32位来说,下标最大为31

这样做的好处是,如果右移多少位即下标,比如00100,1的下标为2,1右移两位就是下标

第二第三第四题是一类问题:是用于为位图做服务的

位图的思想本质就是一个哈希表,只不过哈希是一个数组,但是位图使用bit位,比如之前如果存32个东西,你可能采用32个int,但是位图只需要1个int,32个bit位来表示,题234就是为位图服务的,因为我们有时候要知道位图当中某个位是0还是1,想要修改成0还是1

第六题可以叫做lowbit操作:也就是提取最右侧的1,首先看-n,-n就是就的话就跟原来的n按位取反然后+1,此时发现最右侧的1,左边的区域全部取反,但是右边的区域不变

然后再按位与,因为左边的区域反了按位与就会变成0,但是右边全是0,也不变,按位与之后还是0,所以这样只会留下一个最右侧的1,所以操作就是n&-n

第七题:n-1,因为最右侧的1的右边全是0,所以你-1就会导致他们需要借位,这样就导致右边变成011,所以全都反了过来,导致1的左侧不变,右侧包括1变成了01111……

所以再进行按位与即可

第6和第7题是可以解决leetcode191和338例题和461例题的,可以自己去尝试一下

只要清楚异或是相异为0,和无进位相加即可,123都很好证明,可以自行证明

这里需要注意的是第三点,也就是你一堆数,无论谁和谁先异或都无所谓,不讲究顺序,所以可以利用第一点和第二点,两个相同的异或就会出0,这样就可以消掉重复的数字,如果一个数字和0异或还是本身

leetcode136和260例题可以自己做一些

例题

leetcode面试0101

算法原理讲解:

一、哈希表:时间复杂度O(N),空间复杂度O(N),由于都是小写字母,可以开一个hash[26],空间复杂度O(1)

二、用位图记录,1表示存在,0表示没有

细节就是对应关系,因为a为97,所以一开始算的时候要对应到位图的0位,所以要-97

优化点:鸽巢原理:如果字符串大于26个,肯定有一个重复的,所以可以返回false

class Solution {
public:
    bool isUnique(string astr) {
        if(astr.size()>26) return false;
        int bit=0;
        for(int i=0;i<astr.size();i++){
            int a=(int)astr[i]-97;
            if((bit>>a)&1){
                return false;
            }
            bit|=1<<a;
        }
        return true;
    }
};

leetcode268例题

算法原理讲解:

一、哈希表

二、高斯求和

三、异或运算(直接让数组和0-n的每个数的异或,剩下的就是没出现的)

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int n=nums.size();
        int sum=0;
        for(auto e:nums){
            sum+=e;
        }
        return (1+n)*n/2-sum;
    }
};
class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int ret=0;
        for(auto e:nums){
            ret^=e;
        }
        for(int i=0;i<=nums.size();i++){
            ret^=i;
        }
        return ret;
    }
};

leetcode371例题

算法原理讲解:

异或:无进位相加

异或之后因为是无进位相加,所以我们需要找到哪一位要进位,进位是1+1才需要,所以我们可以把a和b按位与,按位与完之后还是1就代表是要进位的,但是进位是往高位进,所以要左移,左移完之后又要相加,依次重复这个动作,直到进位为0即可

class Solution {
public:
    int getSum(int a, int b) {
        while(1){
            int tmp=a^b;
            int ret=(a&b)<<1;//这里找进位
            int i;
            for(i=0;i<32;i++){
                //检查ret当中是否有1
                if((ret>>i)&1){
                    a=tmp;
                    b=ret;
                    break;
                }
            }
            if(i==32){
                return tmp;
            }
            
        }
    }
};

leetcode137例题

算法原理讲解:意思就是出现3次,你要么3n个0,要么3n个1,所以你对3去模就会得0,所以你统计某一个bit位,如果模了之后=1,说明是目标值做的贡献,如果模了之后=0,说明目标值得这一个比特位为0,没有做贡献

这里可以扩展一下,这类题型目标值出现1次,其他值出现n次,只要模n即可

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int a=0;
        for(int i=0;i<32;i++){
            int sum=0;
            for(int j=0;j<nums.size();j++){
                sum+=(nums[j]>>i)&1;
            }
            if((sum%3)==1){
                a|=1<<i;
            }
        }
        return a;
    }
};

leetcode面试17.19

算法原理讲解:第一步先找到丢失的数字,第二步分类,然后转换成只出现一次的数字

class Solution {
public:
    vector<int> missingTwo(vector<int>& nums) {
        //找到丢失的两个数字的异或
        int tmp=0;
        for(auto e:nums){
            tmp^=e;
        }
        for(int i=1;i<=nums.size()+2;i++){
            tmp^=i;
        }
        int a=0;
        int b=0;
        int diff=0;//把相异的位数存入
        while(1){
            if(tmp>>diff&1) break;
            else diff++;
        }

        //分为两类,为1的放到a里,为0的放b
        for(int i=0;i<nums.size();i++){
            if((nums[i]>>diff)&1) a^=nums[i];
            else b^=nums[i];
        }
         for(int i=1;i<=nums.size()+2;i++){
            if((i>>diff)&1) a^=i;
            else b^=i;
        }
        return {a,b};
    }
};

总结

位运算适合解决以下问题:

  • 涉及 “唯一出现”“次数统计” 的数组问题(异或特性)。
  • 需要直接操作二进制位(判断特征、计数、状态表示)的场景。
  • 追求时间 / 空间效率优化(如 O (1) 空间、O (n) 时间)的问题。

其核心优势是底层操作的高效性,尤其在处理大规模数据或对性能要求严格的场景中表现突出。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值