当前位置: 首页 > news >正文

算法:位运算类型题目练习与总结

前言

这几天连着做了不少位运算的题目,那么这篇博客,就来总结一下这些位运算的题目吧。

位运算的常用小技巧

基础位运算

按位与 & :有0为0,无0为1
按位或 | :有1为1,无1为0
按位异或 ^ :相同为1,不同为0
按位取反 ~ :0变成1,1变成0
左移 << : 二进制序列向左移
右移 >> :二进制序列向右移

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

n & (1 << x)

n >> x 将n的第x位放到最右边第0位
与1 按位与(1的二进制序列为000000…… 1)
即可判断第x位是0还是1

将一个数n的二进制表示中的第x位变成1

n |= (1 << x)

要将第x位变成1,需要第x位按位或1

1 << x 造出了第x位为1,其他位为0的二进制序列

将一个数n的二进制表示中的第x位变成0

n &= ~(1 << x)

要将第x位变成0,需要第x位按位与0

1 << x 造出了第x位为1,其他位为0的二进制序列

对该序列取反,就早出了第x位为0,其他位为1的二进制序列

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

n & (-n)

-n 的本质是将一个二进制数最右边的1的左边区域全部变成相反的数
-n = ~n + 1

eg:
n = 11010001010101100000
-n = 00101110101010011111 + 1
= 00101110101010100000

干掉一个数n的二进制表示中的最右边的1(变成0)

n & (n - 1)

n - 1 的本质是将一个二进制数的最右边的1的右边区域(包括自己)全部取反

eg

n = 11010001010101100000
n - 1 = 11010001010101011111

按位异或(^) 运算律

a ^ a = 0
a ^ 0 = a
a ^ b ^ c = a ^ c ^ b

题目练习

题目一:判定字符是否唯一

题目

在这里插入图片描述

链接:

判定字符是否唯一

思路分析

大家第一眼,这不就hash吗,一分钟搞定,
but,请看限制的第三点:如果不使用额外的数据结构

我们来想想,不使用额外的数据结构,
emm,那就来暴力查找,绷不住了,肯定要想一个时间复杂度优秀的算法啊!

OK,正式开始解决这道题目:

要想时间复杂度优秀,必然是要使用某些数据结构来帮忙的,但是又不允许使用额外的数据结构,
所以,我们可以尝试使用位图的思想

我们把一个比特位作为信息的载体,一个比特位可以存0或1,
我们就用0或1来代表字符串中的字符有没有在之前出现过,
这不就相当于模拟实现了一个hash吗?

一旦想到了这个思想,那这道题就无比简单了

细节:这道题目中只有小写字母,也就是只会出现26种情况,那一个int32位肯定足够存储,
所以位图就选择int,
a — 第0位
b — 第1位
……
依次类推即可

具体代码

    bool isUnique(string astr) {int a = 0;for(auto& e:astr){int i = e - 'a';cout << i << endl;cout << (a & (1 << i)) << endl;if(a & (1 << i)) // 判断第i位是否为1{return false;}elsea |= (1 << i); // 注意这个地方,我想要修改,肯定是 |= 而不是|}return true;}

题目二:丢失的数字

题目

在这里插入图片描述

链接:

丢失的数字

思路分析

经典位运算的题目 — 单身狗。
直接根据按位异或的规律,就可以很简答的解决这道题目。

a ^ a = 0
a ^ 0 = a
a ^ b ^ c = a ^ c ^ b

我们把0~n的所有数字和数组里面的所有数字,全部按位异或一遍,最后得到的结果就是丢失的数字。

具体代码

int missingNumber(vector<int>& nums) {int n = nums.size();int ret = 0;for(int i = 0;i < nums.size();++i){ret ^= i;ret ^= nums[i];}return ret ^= n;}

题目三:137. 只出现一次的数字 II

题目

在这里插入图片描述

链接

137. 只出现一次的数字 II

思路分析

上一道题是单身狗最基础的版本,现在这道题来了一个单身狗的进阶版本。
那么这道题目该怎么做呢?

观察题目,我们可以发现:
3次,1次,那么所有数字的同一位上加起来也肯定是3的倍数啊,
如果不是三个倍数,说明啥,说明1次的这个数字的这一位上也被加进来了,也就是说,出现一次的这个数字的该位上是1

所以,现在,思路已经很明确了,就是把每一位上1出现的次数全部加起来,看看是不是三的倍数,如果不是三的倍数,就把该位设置为1,
也就是 x |= (1<<pos)

最后遍历完32位,就可以得出只出现一次的这个数字了。

具体代码

int singleNumber(vector<int>& nums) {int ret = 0;for(int i = 0;i < 32;++i){int sum = 0;for(auto& e:nums){if(e & (1 << i)) sum++;}//说明这一位是仅出现一次if(sum % 3 != 0) ret |= (1 << i);}return ret;}

题目四:消失的两个数字

题目

在这里插入图片描述

链接

消失的两个数字

思路分析

诶,单身狗又升级了,单身狗2.0版本来了。

这道题又很麻烦了,如果我依旧是把所有的元素全部按位异或,那最后得到的结果不就是两个数的按位异或值吗?

这咋分开啊?

不用担心,肯定是有办法的!

最后两个数肯定是不相同的,所以最后剩下的异或值一定不是0,也就是说最后剩下的异或值最少有一位上是1,
这说明啥?
说明最后的两个数该位上的比特位值不相同,一个0,一个1

这有啥用啊?

不是很显然吗?
我们就可以根据这一位上的不同,把所有的元素分为两个部分
每一个部分都仅有一个元素只出现了一次,这不就又回到了基础单身狗吗?
再按位异或一次呗。

具体代码

    vector<int> missingTwo(vector<int>& nums) {if(nums.size() == 0) return {1,2};int n = nums.size() + 2;int tmp = 0;for(int i = 0;i < nums.size();++i){tmp ^= (i + 1);tmp ^= nums[i];} tmp ^= n;tmp ^= (n - 1);//首先获得特殊的两个数字的异或值,就能知道两者哪一位不同//进而就能把数据分为两类int pos = 0;for(int i = 0;i < 32;++i){if(tmp & (1 << i) ) {pos = i;break;}}cout << pos << endl;int ret1 = 0,ret2 = 0;for(int i = 0;i < nums.size();++i){if((i + 1) & (1 << pos)) ret1 ^= (i + 1);else ret2 ^= (i + 1);//i和1不要写反了if(nums[i] & (1 << pos)) ret1 ^= nums[i];else ret2 ^= nums[i];}if(n & (1 << pos)) ret1 ^= n;else ret2 ^= n;if((n-1) & (1 << pos)) ret1 ^= (n-1);else ret2 ^= (n-1);return {ret1,ret2};}

题目五:371. 两整数之和

题目

在这里插入图片描述

链接

371. 两整数之和

思路分析

这道题目,说实话得对硬件比较了解,
要是笔试真遇到了,同学们还是见仁见智吧,直接不讲武德,return a + b;

好了,开个玩笑,玩归玩,闹归闹,真到了学习的时候,还是认真学习吧。

首先,我们思考一下,按位异或扮演了一个什么样子的角色呢?
1 ^ 1 = 0,
0 ^ 0 = 0,
1 ^ 0 = 1,
0 ^ 1 = 1
嗯?得到的结果不就是不考虑进位的结果吗?
所以按位异或的本质就是无进位相加
a ^ b,也就获得了无进位相加的信息。

于是,我们现在有了加法了,但是我们还缺少进位信息,这咋办。
别慌,我们再来看看&运算
1 & 1 = 1,
0 & 0 = 0,
1 & 0 = 0,
0 & 1 = 0
进位这不就来了吗?只有两位都是1,才得到了1,这个1就是进位的1,
当然,由于是进位,所以,肯定要往高位上加,
所以 (a & b) << 1,也就获得了进位信息

直接 无进位相加的信息和进位信息一起相加,不就是最后的结果吗?

但是还是没有加法啊,所以“进位相加的信息和进位信息一起相加” 中的相加,依然是^ 和 &配合执行,
直到最后没有进位信息,就得到了真正的结果。

具体代码

    int getSum(int a, int b) {while(b)//用b来存储进位信息{int carry = (a & b) << 1;//获取进位信息,并保存a ^= b;//无进位相加,用a来存储结果b = carry;}return a;}
http://www.dtcms.com/a/618466.html

相关文章:

  • 中山网站建设中山国产服务器系统免费的有哪些
  • 【水下目标检测】Yolov8-GDFPN实现水下气泡智能识别系统
  • Python转义字符与原字符
  • 双语网站后台怎么做淄博网站快照优化公司
  • 两学一做知识竞答网站越秀营销型网站建设
  • JavaEE ——多线程的线程安全集合类
  • AI重塑语言教育的深层逻辑:从算法到应用的完整旅程
  • 递归算法精讲:从汉诺塔到反转链表
  • 详解 MySQL 自适应哈希
  • 开平市城乡建设局网站电商网站设计公司
  • 【每天一个AI小知识】:什么是支持向量机(SVM)?
  • AgentOS使用和可视化
  • 定制网站制作广州建设网站设计公司
  • 洛谷 P3935 Calculating——因数个数定理+整除分块(数论分块)
  • 哪里建设网站不会被封设计师网民
  • 人工智能综合项目开发12——模型优化与调优——损失函数
  • Linux系统安装 分区挂载时空间不足且无法回收 问题处理
  • 用curl实现Ollama API流式调用
  • Multisim数据库不可用解决方法
  • 数据库-MySQL-01
  • 怎样制作html个人网站学python能干嘛
  • 用 C 语言破解汉诺塔难题:递归思想的实战演练
  • 【架构解析】深入浅析DeepSeek-V3的技术架构
  • 多模态大模型与单模态大模型的数据准备区别
  • 资讯网站排版wordpress post 类型
  • 开发微网站和小程序安徽建设教育协会网站
  • 利用笔记本与SSH转发给服务器上的虚拟机通网
  • RHEL_NFS服务器
  • 【科研绘图系列】R语言绘制生存曲线图(KW plot)
  • 网站建设订单模板下载做百度推广是不是得有个网站先