位运算总结(Java)

目录

位运算概述

位运算符

位运算的优先级

位运算常见应用

1. 给定一个数n,判断其二进制表示中的第x位是0还是1

 2. 将数n的二进制表示中的第x位修改为1

3. 将数n的二进制表示中的第x位修改为0

4. 位图

例题:判断字符是否唯一

5. 提取数n的二进制表示中的最右侧的1(lowbit)

6. 去掉数n的二进制表示中的最右侧的1

例题:位1的个数

7. 异或(^)运算的运算律

例题:只出现一次的数字

位运算练习

练习1:两整数之和

练习2:只出现一次的数字II

练习3:只出现一次的数字III

练习4:消失的两个数字


位运算概述

位运算:计算机中的数据在内存中是以二进制形式进行存储的,而位运算是对二进制数进行操作的运算,能够按位对数字进行操作

位运算符

(1)与运算符(&):如果两个二进制数的同一位都为1,则结果为1,否则为0。(有0为0)

(2)或运算符(|):如果两个二进制数的同一位至少有一个为1,则结果为1,否则为0。(有1为1)

(3)异或运算符(^):如果两个二进制数的同一位不相同,则结果为1,否则为0。也可以理解为 无进位相加(即0 + 0 = 0;0 + 1 = 1;而当1+1时,结果为0,但不进位)

(4)取反运算符(~):将二进制数的每一位取反,即0变为1,1变为0。

(5)左移运算符(<<):将二进制数向左移动指定的位数,右边补0。

(6)右移运算符(>>):将二进制数向右移动指定的位数,左边补0或者补1(取决于原数字的符号位)。

(7)复合赋值运算符:

&=        a&=b   同      a=a&b

|=         a|=b     同     a=a|b

>>=      a>>=b  同     a=a>>b

<<=      a<<=b  同      a=a<<b

注:对于负数的位操作,一般会使用补码表示法。在补码表示法中,最高位为符号位,0表示正数,1表示负数。位操作对于负数的结果仍然是以补码形式表示的。

位运算的优先级

常见位运算的优先级顺序:

(1)括号(()):括号可以用来改变运算符的结合性和优先级。

(2)取反运算符(~):取反运算符的优先级较高。

(3)左移运算符(<<)、右移运算符(>>)、无符号右移运算符(>>>):左移和右移运算符的优先级相同,且比与、或、异或运算符低。

(4)与运算符(&):与运算符的优先级较低。

(5)异或运算符(^):异或运算符的优先级较低于与运算符。

(6)或运算符(|):或运算符的优先级最低。

注:位运算符的优先级较低,因此在进行位运算操作时,可以使用括号来明确运算符的优先级和结合性

位运算常见应用

1. 给定一个数n,判断其二进制表示中的第x位是0还是1

例如:

在这里,我们从右侧第零位开始计数 

要判断一个数的第x位是0还是1,只需要将x向右移动x位(>> x),再与(&) 上1,即可判断,

(n >> x) & 1 

 2. 将数n的二进制表示中的第x位修改为1

例如:

要将第x位上的0修改为1,我们可以想到或(|)运算符,当两个二进制数的同一位至少有一个为1,则该位结果为1

因此,我们可以将1左移x位再让n与其进行或运算,即n |= (1 << x)

3. 将数n的二进制表示中的第x位修改为0

与将第x位修改为1类似,我们可以想到与(&)运算符,当两个二进制数的同一位至少有一个为0,则该位结果为0

因此,我们将1左移x位,再对其按位取反,这样就使得第x位为0,其他位都为1,然后再进行与运算,即 n &= (~ (1 << x))

4. 位图

位图是一种数据结构,类似于数组,在数组中每个下标标识一个元素,而位图使用每个位来标识一种状态

 以上图为例,int类型的整数有32个比特位,每个比特位标识一个元素,一个元素有两种状态(0或1)

接下来我们以一道例题来进一步了解和使用位图:

例题:判断字符是否唯一

题目链接:

 面试题 01.01. 判定字符是否唯一 - 力扣(LeetCode)

题目描述:

实现一个算法,确定一个字符串 s 的所有字符是否全都不同。

示例 1:

输入: s = "leetcode"
输出: false 

示例 2:

输入: s = "abc"
输出: true

限制:

  • 0 <= len(s) <= 100
  • s[i]仅包含小写字母
  • 如果你不使用额外的数据结构,会很加分。

思路分析:要判断字符串中是否有字符相同,我们可以使用哈希表来统计每个字符的个数,由于字符串中仅包含小写字母,我们可以使用int类型的数组(int[26])来标识26个小写字母(即0下标位置表示a的个数,1下标位置表示b的个数...),然后统计每个字符的个数。

由于只需要判断是否有字符相同,我们也可以使用位图,使用int类型的变量bitMap来作为“数组”,其中26位来标识26个小写字母,当该字符未出现过时(即对应二进制位为0),将对应二进制位修改为1,而当该字符出现过时(即对应二进制位为1),则可判断有字符相同,返回false

代码实现:

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

5. 提取数n的二进制表示中的最右侧的1(lowbit)

要想提取出n中最右侧的1,只需将 n & (-n)

我们可以发现:将 n 转换为 -n(补码) 后,最右侧1 的左边区域(不包括最右侧1)全部变相反,此时,再将n & (-n),就只剩下最右侧的1

6. 去掉数n的二进制表示中的最右侧的1

去掉最右侧的1,只需将 n & (n - 1)

 我们可以发现:将 n 转换为 n-1 后,最右侧1 的右边区域(包括最右侧1)全部变相反,此时,再将n & (n-1),就能够去掉最右侧的1了

我们以一道例题进行练习:

例题:位1的个数

题目链接:

191. 位1的个数 - 力扣(LeetCode)

题目描述:

编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 '1' 的个数(也被称为汉明重量)。

示例 1:

输入:n = 00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。

示例 2:

输入:n = 00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。

示例 3:

输入:n = 11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。

思路分析:

题目要求我们计算整数n的二进制表示中的1的个数,可以通过判断每一位是否为1((n >> x) & 1)求出1的个数,但我们知道n & (n-1)能够去掉最右侧的1,因此我们只需计算能够去掉多少个1,即可求出1的个数

代码实现:

public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int sum = 0;
        while(n != 0){
            n = n & (n-1);
            sum ++;
        }
        return sum;
    }
}

 而题目:

338. 比特位计数 - 力扣(LeetCode)

461. 汉明距离 - 力扣(LeetCode)

也是类似的解题思路,大家可以自行练习

7. 异或(^)运算的运算律

a ^ 0 = a

a ^ a = 0

a ^ b ^ c = a ^ (b ^ c)

我们以一道例题来进一步掌握异或运算的运算律:

例题:只出现一次的数字

题目链接:

136. 只出现一次的数字 - 力扣(LeetCode)

题目描述:

给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。

示例 1 :

输入:nums = [2,2,1]
输出:1

示例 2 :

输入:nums = [4,1,2,1,2]
输出:4

示例 3 :

输入:nums = [1]
输出:1

思路分析:题目告诉我们数组中只有一个元素出现了一次,其他的都出现了两次,且a ^ a = 0,因此我们只需要将数组所有元素异或,出现两次的元素异或后都变为0,最后只剩下出现一次的元素a ^ 0 = a

代码实现:

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

位运算练习

练习1:两整数之和

题目链接:

 371. 两整数之和 - 力扣(LeetCode)

题目描述:

给你两个整数 a 和 b ,不使用 运算符 + 和 - ,计算并返回两整数之和。

示例 1:

输入:a = 1, b = 2
输出:3

示例 2:

输入:a = 2, b = 3
输出:5

思路分析:不能使用运算符 + 和 - ,则我们可以考虑使用位运算来解决这个问题

异或(^)运算相当于无进位相加,在无进位的情况下(0 + 0 = 0,0 + 1 = 1)

接下来,我们考虑进位的情况:当该位上出现 1 + 1 时,则需要进位(即 1 + 1 = 10)而有需要进位的位为(a & b),进位后为(a & b)<< 1

因此,我们可以考虑将整数a 和 b的和,分为无进位加法的结果和进位结果的和

具体过程为:

以15和9为例:

1.先将每一位相加,且不考虑进位。二进制满2进位,即1 + 1 = 10,此时不考虑进位,因此,可以通过异或实现不考虑进位的相加,即 1 + 1 = 0,0 + 0 = 0,1 + 0 = 1,此时1111 + 1001 = 0110

2.找出进位的数。当出现1 + 1时,产生进位,我们可以通过两个数按位与找到这些存在进位的数,再将其向左移动1位,实现进位

3.将1和2得到的数字相加,由于相加不能使用加法,即重复1、2步骤,模拟实现加法,当进位为0时,即不再产生进位,此时异或的结果即为两个数相加的结果

代码实现:

class Solution {
    public int getSum(int a, int b) {
        while(b != 0){
            int carry = (a & b) << 1;//进位
            a = a ^ b;//无进位
            b = carry;
        }
        return a;
    }
}

练习2:只出现一次的数字II

题目链接:

 137. 只出现一次的数字 II - 力扣(LeetCode)

题目描述:

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。

你必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题。

示例 1:

输入:nums = [2,2,3,2]
输出:3

示例 2:

输入:nums = [0,1,0,1,0,1,99]
输出:99

提示:

  • 1 <= nums.length <= 3 * 104
  • -231 <= nums[i] <= 231 - 1

思路分析:由于只有一个元素出现了一次,其他n个元素元素都出现了三次,那么对于每一位都有以下四种情况:

1. 3n个0 + 1个0

2. 3n个0 + 1个1

3. 3n个1 + 1个0

4. 3n个1 + 1个1

对其模3(%3)可得:

1. 3n个0 + 1个0  (%3) -> 0

2. 3n个0 + 1个1  (%3) -> 1

3. 3n个1 + 1个0  (%3) -> 0

4. 3n个1 + 1个1  (%3) -> 1

即只出现一次的元素的二进制位,因此,我们只需求出出现一次的元素的每一位,即可求出该元素 

代码实现:

class Solution {
    public int singleNumber(int[] nums) {
        int ret = 0;
        for(int i = 0; i < 32; i++){
            int count = 0;
            for(int j = 0; j < nums.length; j++){
                count += ((nums[j] >> i) & 1);
            }
            count %= 3;
            ret = ret | (count << i);
        }
        return ret;
    }
}

练习3:只出现一次的数字III

题目链接:

 260. 只出现一次的数字 III - 力扣(LeetCode)

题目描述:

给你一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。

你必须设计并实现线性时间复杂度的算法且仅使用常量额外空间来解决此问题。

示例 1:

输入:nums = [1,2,1,3,2,5]
输出:[3,5]
解释:[5, 3] 也是有效的答案。

示例 2:

输入:nums = [-1,0]
输出:[-1,0]

示例 3:

输入:nums = [0,1]
输出:[1,0]

提示:

  • 2 <= nums.length <= 3 * 104
  • -231 <= nums[i] <= 231 - 1
  • 除两个只出现一次的整数外,nums 中的其他数字都出现两次

思路分析:与只出现一次的数字类似,但此题是两个元素只出现了一次,我们同样可以利用异或的运算律,将所有元素异或,此时得到两个只出现一次的元素的异或结果

如何得到这两个元素呢?

由于这两个元素一定不相同(若相同则异或结果为0,不符合题目中描述),我们可以通过提取结果中最右侧的1(lowbit),并将数组按照该位是否为1分为两组元素,这样,两个只出现一次的数字就被分到了不同的组里,此时再进行异或,就可得到这两个元素

代码实现:

class Solution {
    public int[] singleNumber(int[] nums) {
        int ret = nums[0];
        for(int i = 1; i < nums.length; i++){
            ret ^= nums[i];
        }
        //得到两个数的异或结果
        int ret1 = ret & (-ret);//找到最低位1
        int ret2 = 0;
        for(int i = 0; i < nums.length; i++){
            if((nums[i] & ret1) == 0){
                ret2 ^= nums[i];
            }
        }
        ret1 = ret2 ^ ret;
        return new int[] {ret1, ret2};
    }
}

练习4:消失的两个数字

题目链接:

 面试题 17.19. 消失的两个数字 - 力扣(LeetCode)

题目描述:

给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字。你能在 O(N) 时间内只用 O(1) 的空间找到它们吗?

以任意顺序返回这两个数字均可。

示例 1:

输入: [1]
输出: [2,3]

示例 2:

输入: [2,3]
输出: [1,4]

提示:

  • nums.length <= 30000

思路分析:

数组中缺失两个数字,设数组的长度为len,则所有元素为1 到 len+2,由于其中缺失两个数字,若将数组中所有元素和1 - len + 2看做一个整体,则此时就变为两个数字只出现一次,其他数字出现两次,此时就和 只出现一次的数字III 相同了,接下来按照 只出现一次的数字III 的解题思路来解题就可以解决该问题

代码实现:

class Solution {
    public int[] missingTwo(int[] nums) {
        int len = nums.length;
        int ret = (len + 2) ^ (len + 1);//求出两个数异或的结果
        for(int i = 0; i < nums.length; i++){//所有元素异或结果
            ret ^= nums[i];
            ret ^= (i+1);
        }
        int ret1 = ret & (-ret);//找到最低位1
        int ret2 = 0;
        for(int i = 0; i < len; i++){
            if((nums[i] & ret1) == 0){
                ret2 ^= nums[i];
            }
        }
        for(int i = 1; i <= len + 2; i++){
            if((i & ret1) == 0){
                ret2 ^= i;
            }
        }
        ret1 = ret2 ^ ret;
        return new int[] {ret1, ret2};
    }
}

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

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

相关文章

【开源】SpringBoot框架开发企业项目合同信息系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 合同审批模块2.3 合同签订模块2.4 合同预警模块2.5 数据可视化模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 合同审批表3.2.2 合同签订表3.2.3 合同预警表 四、系统展示五、核心代码5.1 查询合同…

C++ JSON解析

JSON解析 JSONCPPC实现JSON解析器 JSONCPP JSONCPP源码链接&#xff1a;https://github.com/open-source-parsers/jsoncpp JSOCPP源码下载以后&#xff0c;首先复制一份include文件夹下的json文件夹&#xff0c;头文件留着后续备用。 使用Cmake生成项目。在IDE中编译jsoncpp_…

【算法分析与设计】环形链表

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;算法分析与设计 ⛺️稳中求进&#xff0c;晒太阳 题目 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次…

缓慢变化维 常用的处理方法

什么是缓慢变化维 维度 在数仓中&#xff0c;表往往会被划分成两种类型&#xff0c;一种是 事实表&#xff0c;另一种是维度表&#xff0c;举个例子&#xff0c;比如说&#xff1a; ❝ 2024年2月14日&#xff0c;健鑫在12306上买了两张火车票&#xff0c;每张火车票400元&…

TinUI v5预发布记录

TinUI v5预发布记录 前言新控件滚动选择框菜单按钮 新样式pre1pre2pre3 新功能导入字体文件 前言 TinUI是一个从2021年正式开始并一直维护到现在的小项目&#xff0c;中间经过了四代版本的更新。因为一些原因&#xff0c;2023年&#xff0c;TinUI-4后更新较少。 TinUI发展历程…

jmeter-问题二:JMeter进行文件上传时,常用的几种MIME类型

以下是一些常用的MIME类型及其对应的文件扩展名&#xff1a; 文本类型: text/plain: 通常用于纯文本文件&#xff0c;如 .txt 文件。 text/html: 用于HTML文档&#xff0c;即 .html 文件。 application/msword: Microsoft Word文档&#xff0c;即 .doc 和 .docx 文件。 图像…

英伟达市值超越谷歌!老黄隔空回应Altman的巨资筹款计划:没必要,真的没必要!

凭借算力上的霸主地位&#xff0c;英伟达正稳步成为科技领域的下一个巨头&#xff0c;在不久的15个月前&#xff0c;英伟达的市值还不足3000亿美元。然而&#xff0c;截至昨日&#xff0c;英伟达股价飙升使其市值达到了1.83万亿美元&#xff0c;超越了Alphabet&#xff08;谷歌…

传输层DoS

传输层是国际标准化组织提出的开放系统互联参考模型&#xff08;OSI&#xff09;中的第四 层。该层协议为网络端点主机上的进程之间提供了可靠、有效的报文传送服务。 平时我们所谈论的拒绝服务攻击大多是基于TCP的&#xff0c;因为现实中拒绝服务的对象 往往都是提供HTTP服务的…

Java类加载

Java类加载机制是Java虚拟机&#xff08;JVM&#xff09;的一个核心组成部分&#xff0c;它负责将Java类从不同的数据源&#xff08;如本地文件系统、网络等&#xff09;加载到JVM中&#xff0c;并为之生成对应的java.lang.Class对象。理解Java类加载机制对于深入理解Java运行时…

Python中多种生成随机密码超实用实例

前言 密码是信息安全的基石&#xff0c;它用于保护我们的账户、数据和隐私。为了确保密码足够强大&#xff0c;需要生成随机密码。在本文中&#xff0c;将讨论多种Python方法&#xff0c;用于生成随机密码的实用示例和技巧。 目录 ​编辑 前言 密码生成的要求 使用secrets…

创新S3存储桶检索:Langchain社区S3加载器搭载OpenAI API

在瞬息万变的数据存储和处理领域&#xff0c;将高效的云存储解决方案与先进的 AI 功能相结合&#xff0c;为处理大量数据提供了一种变革性的方法。本文演示了使用 MinIO、Langchain 和 OpenAI 的 GPT-3.5 模型的实际实现&#xff0c;重点总结了存储在 MinIO 存储桶中的文档。 …

C++ 音视频原理

本篇文章我们来描述一下音视频原理 音视频录制原理: 下面是对这张思维导图的介绍 摄像头部分: 麦克风采集声音 摄像头采集画面 摄像头采集回来的数据可以用RGB也可以用YUV来表示 图像帧帧率 一秒能处理多少张图像 图像处理 &#xff1a;调亮度 图像帧队列 :意思是将数据取…

【51单片机】LCD1602(江科大)

1.LCD1602介绍 LCD1602(Liquid Crystal Display)液晶显示屏是一种字符型液晶显示模块,可以显示ASCII码的标准字符和其它的一些内置特殊字符,还可以有8个自定义字符 显示容量:162个字符,每个字符为5*7点阵 2.引脚及应用电路 3.内部结构框图 屏幕: 字模库:类似于数码管的数…

【JVM篇】什么是jvm

文章目录 &#x1f354;什么是Java虚拟机&#x1f6f8;Java虚拟机有什么用&#x1f339;Java虚拟机的功能&#x1f388;Java虚拟机的组成 &#x1f354;什么是Java虚拟机 JVM指的是Java虚拟机&#xff0c;本质上是一个运行在计算机上的程序&#xff0c;可以运行 Java字节码文件…

微信小程序开发学习笔记《17》uni-app框架-tabBar

微信小程序开发学习笔记《17》uni-app框架-tabBar 博主正在学习微信小程序开发&#xff0c;希望记录自己学习过程同时与广大网友共同学习讨论。建议仔细阅读uni-app对应官方文档 一、创建tabBar分支 运行如下的命令&#xff0c;基于master分支在本地创建tabBar子分支&#x…

Spring Boot3自定义异常及全局异常捕获

⛰️个人主页: 蒾酒 &#x1f525;系列专栏&#xff1a;《spring boot实战》 &#x1f30a;山高路远&#xff0c;行路漫漫&#xff0c;终有归途。 目录 前置条件 目的 主要步骤 定义自定义异常类 创建全局异常处理器 手动抛出自定义异常 前置条件 已经初始化好一个…

17 ABCD数码管显示与动态扫描原理

1. 驱动八位数码管循环点亮 1.1 数码管结构图 数码管有两种结构&#xff0c;共阴极和共阳极&#xff0c;ACX720板上的是共阳极数码管&#xff0c;低电平点亮。 1.2 三位数码管等效电路图 为了节约I/O接口&#xff0c;各个数码管的各段发光管被连在一起&#xff0c;通过sel端…

【Spring框架】Spring事务同步

目录 一、什么是Spring事务同步 二、 事务同步管理器 2.1 TransactionSynchronizationManager事务同步管理器 2.1.1 资源同步 2.1.2 事务同步 2.1.3 总结 三、事务同步管理器保障事务的原理 四、spring事务为何使用TransactionSynchronizationManager spring源码实现 …

详解CC++内存管理(new和delete)

文章目录 写在前面1. C&C内存分布2. C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free3. C内存管理方式&#xff08;语法&#xff09;3.1 new/delete操作内置类型3.2 new和delete操作自定义类型 4. new和delete的实现原理4.1 operator new与operator delete…

企业架构师的人格特质

L - Learning 持续学习的能力A - Abstracting 概念抽象的能力C1 - Connecting 联结事物的能力C2 - Compromising 平衡折衷的能力D - Decisioning 果断决策的能力 参考文章的链接
最新文章