位运算详解

简介: 本文介绍了位运算符及其基本操作,并通过几个例题详细解析了位运算的应用。内容包括左移`<<`、右移`>>`、按位取反`~`、与运算`&`、或运算`|`和异或运算`^`等运算符的使用方法。基本操作部分展示了如何检查和修改二进制位,以及异或运算的性质。例题部分则通过判定字符是否唯一、丢失的数字、两整数之和和消失的两个数字等问题,具体说明了位运算的实际应用技巧。

1. 运算符

<<:左移运算符,左边移出的位被丢弃,右边空出的位用 0 填充。

>>:右移运算符,将左边的操作数的二进制表示向右移动右边操作数指定的位数。

~:按位取反,将操作数的二进制表示中的每一位进行取反操作

&:与运算,有 0 结果就是 0

|:或运算,有 1 结果就是 1

^:亦或,相同为 0,相异为 1 / 不进位相加

2. 基本操作

191,338,461,136,260

给定一个数 n,确定它的二进制表示中第 x 位是 0 还是 1

(n >> x) & 1

给定一个数 n,把它的二进制表示的第 x 位修改为 1

n |= (1 << x)

给定一个数 n,把它的二进制表示的第 x 位修改为 0

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

提取一个数 n 二进制表示中最右边的 1

n & -n ( -n 就是将最右侧的 1 左边全部变成相反的,右边不变)

给定一个数 n ,把最右侧的 1 变成 0

n & (n - 1)

亦或(^)运算符运算律

  1. a ^ a = 0
  2. a ^ 0 = a
  3. a ^ b ^ c = a ^ (b ^ c)

3. 例题解析

a. 面试题 01.01. 判定字符是否唯一

面试题 01.01. 判定字符是否唯一

如果使用数据结构的话很明显就是放到哈希表中,然后再遍历哈希表进行查找,如果不使用数据结构的话可以通过使用位图的方式来替代哈希表,26 个比特位代表 26 个字母(1 表示该字符已经出现过,0 就表示没有出现过),在遍历字符串的同时进行判断,如果说出现过就直接放回 false ,如果没有就把该位设置为 1

class Solution {
    public boolean isUnique(String astr) {
        if(astr.length() > 26) return false;
        char[] chars = astr.toCharArray();
        int bitMap = 0;
        for(int i = 0;i < chars.length;i++){
            int x = chars[i] - 'a';
            if((bitMap >> x & 1) == 1) return false;
            else bitMap |= (1 << x);
        }
        return true;
    }
}

b. 丢失的数字

268. 丢失的数字

方法一:使用哈希表,遍历数组,把数组中的数存在哈希表中并设为 1,再从 0 ~ n 进行遍历,查询哪一位为 0

方法二:高斯求和,把 0 ~ 1 的数进行求和,之后再把数组中的元素都减掉,最后剩的即为所求

方法三:位运算

先把 0 ~ 1的数都进行亦或运算,然后再遍历数组,再进行一次亦或运算,由于 a ^ a = 0,所以重复出现的都被变为 0 了,最后再根据 a ^ 0 = a 知道,剩余的数肯定就是答案了

class Solution {
    public int missingNumber(int[] nums) {
        int ans = 0;
        for(int i = 0;i <= nums.length;i++){
            ans ^= i;
        }
        for(int i = 0;i < nums.length;i++){
            ans ^= nums[i];
        }
        return ans;
    }
}

c. 两整数之和

371. 两整数之和

由于题中限制了不能使用 + ,- ,所以可以考虑位运算来解决,在开始提到过亦或操作可以看做是无进位相加,所以只需要把进位找到就行,经过分析发现,只有 1 和 1 的情况会出现进位,也就符合了 & 运算,但此时只是表示哪一位发生了进位,所以需要把结果左移再加上无进位相加的结果即可,不过此时还可能会出现进位,所以上述操作需要重复计算,直到没有进位为止

class Solution {
    public int getSum(int a, int b) {
        while(b != 0){
            int ans = a ^ b;
            int up = (a & b) << 1;
            a = ans;
            b = up;
        }
        return a;
    }
}

d. 只出现一次的数字 II

137. 只出现一次的数字 II

从每一个二进制位相加开始看,所有的数相加最后会有四种情况,把这些情况都 % 3 之后发现,结果和最后剩余那个只出现一次的数的位置相同,所以也就可以从每一个二进制位入手,把所有的数字加起来 % 3,得出每一位的结果,并把目标位设置为结果,最后就是只出现一次的那个数

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

e. 面试题 17.19. 消失的两个数字

面试题 17.19. 消失的两个数字

如果这道题转换一下,先把从 1 到 N 的数亦或,再把结果和 nums 数组亦或,结果也就变成了 nums 数组的数出现了两次,其他的数(也就是缺失的数)出现了一次,就转化为了求消失的两个数字,最终的亦或结果是缺失的那两个数的亦或结果,其中一定可以找到二进制位为 1 的那一位,也就可以把所有的数根据这个位置分为两组,这样也就能够把缺失的这两个数分开,就转化为了找缺失的一位的数,把所有结果亦或,最终就是缺失的那个数字,再求另一组即可

class Solution {
    public int[] missingTwo(int[] nums) {
        int tmp = 0;
        //把 1 ~ n 的数亦或
        for(int i = 1;i <= nums.length + 2;i++){
            tmp ^= i;
        }
        //加上数组的数亦或
        for(int i = 0;i < nums.length;i++){
            tmp ^= nums[i];
        }
        //找到第一个为 1 的二进制位
        int diff = 0;
        while(true){
            if(((tmp >> diff) & 1) == 1) break;
            else diff++;
        }
        int[] ans = new int[2];
        //数组的数分组亦或
        for(int i = 0;i < nums.length;i++){
            if(((nums[i] >> diff) & 1) == 1) ans[0] ^= nums[i];
            else ans[1] ^= nums[i];
        }
        //1 ~ n 的数分组亦或
        for(int i = 1;i <= nums.length + 2;i++){
            if(((i >> diff )& 1) == 1) ans[0] ^= i;
            else ans[1] ^= i;
        }
        return ans;
    }
}
相关文章
|
16天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
13天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2547 19
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
|
13天前
|
机器学习/深度学习 算法 数据可视化
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
2024年中国研究生数学建模竞赛C题聚焦磁性元件磁芯损耗建模。题目背景介绍了电能变换技术的发展与应用,强调磁性元件在功率变换器中的重要性。磁芯损耗受多种因素影响,现有模型难以精确预测。题目要求通过数据分析建立高精度磁芯损耗模型。具体任务包括励磁波形分类、修正斯坦麦茨方程、分析影响因素、构建预测模型及优化设计条件。涉及数据预处理、特征提取、机器学习及优化算法等技术。适合电气、材料、计算机等多个专业学生参与。
1543 16
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
|
9天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
11天前
|
人工智能 IDE 程序员
期盼已久!通义灵码 AI 程序员开启邀测,全流程开发仅用几分钟
在云栖大会上,阿里云云原生应用平台负责人丁宇宣布,「通义灵码」完成全面升级,并正式发布 AI 程序员。
|
15天前
|
编解码 JSON 自然语言处理
通义千问重磅开源Qwen2.5,性能超越Llama
击败Meta,阿里Qwen2.5再登全球开源大模型王座
698 14
|
10天前
|
人工智能 开发框架 Java
重磅发布!AI 驱动的 Java 开发框架:Spring AI Alibaba
随着生成式 AI 的快速发展,基于 AI 开发框架构建 AI 应用的诉求迅速增长,涌现出了包括 LangChain、LlamaIndex 等开发框架,但大部分框架只提供了 Python 语言的实现。但这些开发框架对于国内习惯了 Spring 开发范式的 Java 开发者而言,并非十分友好和丝滑。因此,我们基于 Spring AI 发布并快速演进 Spring AI Alibaba,通过提供一种方便的 API 抽象,帮助 Java 开发者简化 AI 应用的开发。同时,提供了完整的开源配套,包括可观测、网关、消息队列、配置中心等。
529 8
|
3天前
|
Docker 容器
Docker操作 (五)
Docker操作 (五)
141 68
|
3天前
|
Docker 容器
Docker操作 (三)
Docker操作 (三)
132 69
|
15天前
|
人工智能 自动驾驶 机器人
吴泳铭:AI最大的想象力不在手机屏幕,而是改变物理世界
过去22个月,AI发展速度超过任何历史时期,但我们依然还处于AGI变革的早期。生成式AI最大的想象力,绝不是在手机屏幕上做一两个新的超级app,而是接管数字世界,改变物理世界。
561 49
吴泳铭:AI最大的想象力不在手机屏幕,而是改变物理世界