算法从入门到精通——位运算

文章目录

◆ 博主名称:此生决int

大家好,欢迎来到我的博客~

⭐ 个人专栏:快速复习系列

⭐ 热门专栏:蓝桥杯/ACM & 面试算法入门到进阶

上期回顾

上期我们主要介绍了前缀和

文章概要

本文主要介绍了位运算及相关性质,然后通过五道算法题来巩固对位运算的理解!

位运算

位运算简介及常用性质总结

基础位运算

1,按位与:& 有0则为0

2,按位或:| 有1则为1

3,按位异或:^ 同为0,异为1

4,按位取反:~ 1变0,0变1

对于一个数n的第X位

获取它是0还是1(n&=(1<<x))

因为是获取嘛,所以,其他位是什么就不重要了,最后的结果肯定是想要得到00000...010...000这样的效果的,所以,我们要选有0就为0的&(按位与)。

修改它为1(n|=(1<<x))

修改就要保证其他位不变,又要改为1,所以,有用有1就为1的 | (按位或),那么我们就要按位或一个这样的数000...010...000,让1左移x位即可.

修改它为0(n&=(~(1<<x)))

要改为0,就只能用(有0则0)&,然后又要保证其他位不变,所以要&上一个111...101...111这样的数,让1左移x位再取反即可。

一个数二进制表示里最右边的1

提取它(n&(--n))

对于-n,--n的效果是让一个数的二进制的最右边的1左边的数全部变成相反,(不包括那个1)

所以,让n&--n,那个1,右边全是0,还是0,左边一定两两相反,所以&后就是0啦。

干掉它(n&n--1)

对于n-1,n--1他可以把那个1的右边全变成相反,包括那个1,所以&完之后就全变成0啦。

^ 的性质(交换律,结合律,相同 ^ 为0)

1,a^0=a

2,a^a=0

3,a ^ b ^ c=a ^ (b ^ c)

本章算法题的简单总结(建议最后看)

1,判定字符是否唯一⭐️(位图的思想)

2,丢失的数字⭐️(按位异或的性质)

3,两整数之和⭐️⭐️⭐️(加法的模拟加位运算基础性质)

4,只出现一次的数字 II⭐️⭐️⭐️(思维+比特位的控制)

5,消失的两个数字⭐️⭐️⭐️⭐️

1,判定字符是否唯一⭐️

题目链接:

判定字符是否唯一

解题思路

1,位图的思想

1,首先肯定想到哈希表,然后,都是小写字母,所以,可以用数组来存,再优化,可以用一个int来存(位图)

由于字符串只包含小写字母,因此可以使用一个 int 的二进制位表示字符是否出现过。第 i 位为 1 表示字符已经出现,再次出现则返回 false

解题代码

cpp 复制代码
class Solution {
public:
    bool isUnique(string astr) {

        //哈希表->数组代替->位图

        //n 的二进制位表示字符是否出现
        int n=0;

        for(auto it:astr)
        {
            //计算当前字符对应哪一位
            int i=it-'a';

            //这一位已经是 1
            //说明字符重复出现
            if(n&(1<<i))
                return false;
            else
                //把这一位改成 1
                n|=(1<<i);
        }

        return true;
    }
};

没懂?看看大神的解题代码!!

大神解题代码

cpp 复制代码
class Solution {
public:
    bool isUnique(string astr) {

        //一个 int 有 32 位
        //可以表示 26 个小写字母是否出现

        int mask=0;

        for(auto c:astr)
        {
            //得到当前字符对应的二进制位
            int bit=1<<(c-'a');

            //这一位已经出现过
            if(mask&bit)
                return false;

            //标记当前字符出现
            mask|=bit;
        }

        return true;
    }
};

2,丢失的数字⭐️

题目链接:

丢失的数字

解题思路

异或的性质

利用异或性质:相同数字异或两次会变成 0。将数组中的元素和 0~n 全部异或,最后剩下的就是缺失的数字。

解题代码

cpp 复制代码
class Solution {
public:
    int missingNumber(vector<int>& nums) {
        //ret 保存异或结果
        int ret=0;
        for(int i=0;i<nums.size();i++)
        {
            //异或数组元素
            ret^=nums[i];
            //异或下标
            ret^=i;
        }
        //最后再异或 n
        return ret^nums.size();
    }
};

没懂?看看大神的解题代码!!

大神解题代码

cpp 复制代码
class Solution {
public:
    int missingNumber(vector<int>& nums) {
        //先把答案初始化为 n
        int ret=nums.size();
        for(int i=0;i<nums.size();i++)
            //同时异或下标和数组元素
            ret^=i^nums[i];
        return ret;
    }
};

3,两整数之和⭐️⭐️⭐️

题目链接:

两整数之和

解题思路

利用位运算模拟加法。a^b 可以得到不进位的结果,(a&b)<<1 可以得到进位结果。不断循环相加,直到没有进位为止。

解题代码

cpp 复制代码
class Solution {
public:
    int getSum(int a, int b) {

        //模拟加法

        //a^b 得到不进位结果
        //(a&b)<<1 得到进位结果

        //不断相加直到没有进位
        while(b)
        {
            //不进位的和
            int x=a^b;

            //进位
            int y=(a&b)<<1;

            a=x;
            b=y;
        }

        return a;
    }
};

没懂?看看大神的解题代码!!

大神解题代码

cpp 复制代码
class Solution {
public:
    int getSum(int a, int b) {

        //b 表示进位
        while(b)
        {
            //carry 保存进位
            unsigned carry=(unsigned)(a&b)<<1;

            //计算不进位结果
            a^=b;

            //更新进位
            b=carry;
        }

        return a;
    }
};

4,只出现一次的数字 II⭐️⭐️⭐️

题目链接:

只出现一次的数字 II

解题思路

由于除了一个数字只出现一次,其余数字都出现三次,因此统计每一比特位上 1 出现的次数,再对 3 取模。最后剩下的位就是只出现一次数字的二进制位。

解题代码

cpp 复制代码
class Solution {
public:
    int singleNumber(vector<int>& nums) {

        //arr[i] 表示第 i 位 1 出现的次数
        int arr[32]={0};

        //统计每一位 1 的个数
        for(int i=0;i<nums.size();i++)
        {
            for(int j=0;j<32;j++)
            {
                //对 3 取模
                arr[j]=(arr[j]+((nums[i]>>j)&1))%3;
            }
        }

        int ret=0;

        //a 表示当前位权值
        long long a=1;

        //还原答案
        for(int i=0;i<32;i++)
        {
            //这一位存在
            if(arr[i])
                ret+=a;

            a*=2;
        }

        return ret;
    }
};

没懂?看看大神的解题代码!!

大神解题代码

cpp 复制代码
class Solution {
public:
    int singleNumber(vector<int>& nums) {

        int ret=0;

        //枚举每一位
        for(int i=0;i<32;i++)
        {
            int count=0;

            //统计这一位 1 的个数
            for(auto x:nums)
                count+=(x>>i)&1;

            //不能被 3 整除
            //说明答案这一位是 1
            if(count%3)
                ret|=(1<<i);
        }

        return ret;
    }
};

5,消失的两个数字⭐️⭐️⭐️⭐️

题目链接:

消失的两个数字

解题思路

先将数组元素和 1~n 全部异或,得到缺失的两个数字异或结果。由于这两个数字不同,因此异或结果某一位一定为 1 。根据这一位是否为 1 分组,分为与a一样或与b一样,再分别异或即可得到两个缺失数字。

解题代码

cpp 复制代码
class Solution {
public:
    vector<int> missingTwo(vector<int>& nums) {

        //tmp 为两个缺失数字异或结果
        int tmp=0;

        //异或数组元素
        for(auto it:nums)
            tmp^=it;

        //异或 1~n
        for(int i=1;i<=nums.size()+2;i++)
            tmp^=i;

        //找到第一个不同位
        int diff=0;

        while(1)
        {
            //这一位为 1
            if((tmp>>diff)&1)
                break;
            else
                diff++;
        }

        int a=0,b=0;

        //按 diff 位是否为 1 分组
        for(auto it:nums)
        {
            if((it>>diff)&1)
                a^=it;
            else
                b^=it;
        }

        //继续异或 1~n
        for(int i=1;i<=nums.size()+2;i++)
        {
            if((i>>diff)&1)
                a^=i;
            else
                b^=i;
        }

        return {a,b};
    }
};

没懂?看看大神的解题代码!!

大神解题代码

cpp 复制代码
class Solution {
public:
    vector<int> missingTwo(vector<int>& nums) {

        //xorSum 为两个缺失数字异或结果
        int xorSum=0;

        for(auto x:nums)
            xorSum^=x;

        for(int i=1;i<=nums.size()+2;i++)
            xorSum^=i;

        //lowbit 取出最低位的 1
        int lowbit=xorSum&-xorSum;

        int a=0,b=0;

        //按 lowbit 分组
        for(auto x:nums)
        {
            if(x&lowbit)
                a^=x;
            else
                b^=x;
        }

        //继续异或 1~n
        for(int i=1;i<=nums.size()+2;i++)
        {
            if(i&lowbit)
                a^=i;
            else
                b^=i;
        }

        return {a,b};
    }
};

下期预告

结语

本期内容就到这里啦,欢迎大家在评论区一起交流讨论

如果你也在为蓝桥杯/ACM备赛头疼,或是准备算法面试找不到系统学习路径,欢迎订阅我的「算法从入门到精通」专栏

这里没有枯燥的理论堆砌,只有完整的算法学习路线

搭配精选梯度习题+清晰思路解析,帮你把每个算法学透、练熟。包教包会的!

我们一起在算法路上稳步进阶!

相关推荐
汉克老师5 小时前
GESP6级C++考试语法知识(二十七、广度优先搜索(二、二维BFS))
c++·算法·图论·宽度优先·广度优先搜索·gesp6级·gesp六级
春栀怡铃声5 小时前
【C++修仙录02】筑基篇:vector 使用
开发语言·c++·算法
Loli_Wolf5 小时前
AI 原生研发闭环:从提需到线上监测,再自动回到提需
人工智能·深度学习·算法·microsoft·ai·ai编程·harness
丁劲犇5 小时前
使用TraeAI开发Web页面测试MSYS2 ucrt64 Qt MCP服务器
服务器·前端·c++·qt·mcp
计算机安禾5 小时前
【算法分析与设计】第4篇:分治策略的理论框架与经典案例
数据结构·算法·排序算法
Kiling_07045 小时前
面向对象和集合编程题 ( 二 )
java·开发语言·数据结构·算法
过期动态5 小时前
【LeetCode 热题 100】两数之和— 暴力法与哈希表法详解
java·数据结构·算法·leetcode·散列表
Pointer Pursuit5 小时前
哈希表的实现
数据结构·哈希算法·散列表