位运算总结(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 valuepublic 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);//找到最低位1int 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);//找到最低位1int 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.hqwc.cn/news/468509.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

寒假思维训练day21

今天更新一道不错的状态压缩DP题&#xff0c;顺带总结一下状态压缩DP。 摘要&#xff1a; Part1 浅谈状态压缩DP的理解 Part2 浅谈对状态机DP的理解 Part3 关于状态压缩DP的1道例题 Part1 状态压缩DP 1、状态压缩DP&#xff1a; 事物的状态可能包含多个特征&#xff0c;…

伦敦金是现货黄金吗?

伦敦金属现货黄金交易的一种形式。投资者可以通过伦敦金市场直接买卖黄金&#xff0c;以实现投资收益。伦敦金市场具有高度流动性和透明度&#xff0c;是全球投资者广泛参与的贵金属交易市场之一。 什么是现货黄金&#xff1f; 现货黄金是指实物黄金的交易&#xff0c;投资者可…

【STM32 CubeMX】HAL库的本质读写寄存器

文章目录 前言一、HAL库的本质1.1 HAL库的本质是操作寄存器1.2 自己实现HAL_GPIO_WritePin寄存器通过寄存器的操作点灯代码概况Port bit set/reset register寄存器 总结 前言 在嵌入式系统开发中&#xff0c;HAL&#xff08;Hardware Abstraction Layer&#xff09;库是一个重…

算法沉淀——哈希算法(leetcode真题剖析)

算法沉淀——哈希算法 01.两数之和02.判定是否互为字符重排03.存在重复元素04.存在重复元素 II05.字母异位词分组 哈希算法&#xff08;Hash Algorithm&#xff09;是一种将任意长度的输入&#xff08;也称为消息&#xff09;映射为固定长度的输出的算法。这个输出通常称为哈希…

CTFshow web(php命令执行59-67)

web59 <?php /* # -*- coding: utf-8 -*- # Author: Lazzaro # Date: 2020-09-05 20:49:30 # Last Modified by: h1xa # Last Modified time: 2020-09-07 22:02:47 # email: h1xactfer.com # link: https://ctfer.com */ // 你们在炫技吗&#xff1f; if(isset($_POST…

docker 2:安装

docker 2&#xff1a;安装 ‍ ubuntu 安装 docker sudo apt install docker.io‍ 把当前用户放进 docker 用户组&#xff0c;避免每次运行 docker 命都要使用 sudo​ 或者 root​ 权限。 sudo usermod -aG docker $USER​id $USER ​看到用户已加入 docker 组 ​​ ‍ …

SCI 1区论文:Segment anything in medical images(MedSAM)[文献阅读]

基本信息 标题&#xff1a;Segment anything in medical images中文标题&#xff1a;分割一切医学图像发表年份: 2024年1月期刊/会议: Nature Communications分区&#xff1a; SCI 1区IF&#xff1a;16.6作者: Jun Ma; Bo Wang(一作&#xff1b;通讯)单位&#xff1a;加拿大多…

随机过程及应用学习笔记(二)随机过程的基本概念

随机过程论就是研究随时间变化的动态系统中随机现象的统计规律的一门数学学科。 目录 前言 一、随机过程的定义及分类 1、定义 2、分类 二、随机过程的分布及其数字特征 1、分布函数 2、数字特征 均值函数和方差函数 协方差函数和相关函数 3、互协方差函数与互相关函…

Spring 如何解决循环依赖?Spring三级缓存

什么是循环依赖 说白是一个或多个对象实例之间存在直接或间接的依赖关系&#xff0c;这种依赖关系构成了构成一个环形调用。 自己依赖自己 两个对象间的依赖关系 多个对象间的依赖关系 Spring出现循环依赖的场景 单例的setter注入 Service public class A {Resourceprivate…

深刻反思现代化进程:20世纪与21世纪的比较分析及东西方思想家的贡献

深刻反思现代化进程&#xff1a;20世纪与21世纪的比较分析及东西方思想家的贡献 摘要&#xff1a;随着人类社会的快速发展&#xff0c;现代化已成为全球范围内的普遍追求。然而&#xff0c;20世纪至21世纪的现代化进程并非一帆风顺&#xff0c;它伴随着环境破坏、社会不平等和文…

Ubuntu 23.10通过APT安装Open vSwitch

正文共&#xff1a;888 字 8 图&#xff0c;预估阅读时间&#xff1a;1 分钟 先拜年&#xff01;祝各位龙年行大运&#xff0c;腾跃展宏图&#xff01; 之前在介绍OpenStack的时候介绍过&#xff08;什么是OpenStack&#xff1f;&#xff09;&#xff0c;OpenStack是一个开源的…

【DDD】学习笔记-订单核心流程的事件风暴

现在&#xff0c;我们针对电商领域的订单核心流程开展事件风暴工作坊&#xff0c;以此来获得我们的领域分析模型。 工作坊准备 要开展好事件风暴工作坊&#xff0c;需得具备天时地利人和。 天时地利人和 天时。需得安排好专门的时间来一个为期多天的事件风暴。事件风暴很难…