算法之位运算

常见的位运算操作:

首先先熟悉一下常见的位运算操作: 

1. 基础位运算

左移<<, 右移>>, 按位与&, 按位或|, 按位异或^, 按位取反~

注意: 异或其实是一种无进位相加. 

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

n & (1<<x) 或者 (n>>x) & 1

3. 将一个数 n 的二进制表示的第 x 位修改成 1

n |= (1<<x)

4. 将一个数 n 的二进制表示的第 x 位修改成 0

n &= ~(1<<x)

5. 位图的思想

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

n & -n

7. 干掉一个数n二进制表示中最右端的1--- Brian Kernighan 算法

对于任意整数 x, 令 x = x & (x−1), 该运算将 x 的二进制表示的最后一个 1 变成 0. 

8 位运算的优先级

不确定就加括号

9 异或运算的运算律

1.交换律:a ^ b = b ^ a

2.结合律:a ^ (b ^ c) = (a ^ b) ^ c

3.自反性:a ^ a = 0,a ^ 0 = a


 练习1: 位1的个数

方法1:

可以直接循环检查给定整数 n 的 32 个二进制位的每一位是否为 1, 当检查第 i 位时, 我们可以让 n 与 1<<i进行与运算, 当且仅当 n 的第 i 位为 1 时, 运算结果不为 0.

class Solution {
public:int hammingWeight(uint32_t n) {int ret = 0;for(int i = 0; i < 32; i++)if(n & (1 << i))ret++;return ret;}
};

方法2: 位运算的优化

利用之前总结的性质, n & (n-1) 可以将最后一个1消除, 所以利用循环每次消除n最右侧的一个1, 循环执行的次数就是1的个数:

class Solution {
public:int hammingWeight(uint32_t n) {int ret = 0;while(n){n &= n-1;ret++;}return ret;}
};

练习2: 比特位计数

利用上一题的函数, 依次计算n个数中位1的个数即可:

class Solution {
public:int hammingWeight(uint32_t n) {int ret = 0;while(n){n &= n-1;ret++;}return ret;}vector<int> countBits(int n) {vector<int> ret;for(int i = 0;i<=n;i++){ret.push_back(hammingWeight(i));}return ret;}
};


练习3: 汉明距离

计算 x 和 y 之间的汉明距离,可以先计算 x⊕y , 然后统计 x⊕y 中 1 的位数.

class Solution {
public:int hammingDistance(int x, int y) {int n = x^y, ret = 0;while(n){n &= n-1;ret++;}return ret;}
};

练习4: 只出现一次的数字 

利用异或运算的自反性, 很容易写出: 

class Solution {
public:int singleNumber(vector<int>& nums){int ret=0;for(auto e:nums)ret^=e;return ret;}
};

 


练习5: 只出现一次的数组3

此题和上一题不同, nums中有两个出现一次的数字, 不能直接用上一题的方法, 但是思考: 所有数异或起来的结果有没有什么特点呢?

出现两次的数字一定都两两相消了, 两个不同的数字它们一定有至少一个比特位是不同的, 也就是异或和结果一定有一位是1.

对于这一个比特位, 数组中的所有数要么这一位为0, 要么这一位为1, 用这个特性就可以把数组的数分为两组, 每组求一遍异或和, 结果就是只出现一次的那个数字, 只需要找到那个比特位为1的位置即可. 

class Solution {
public:vector<int> singleNumber(vector<int>& nums) {int sum = 0;for(auto e: nums)sum ^= e;int pos = 0;//计算右边第一个1的位置for(int i = 0; i < 32;i++){if(sum>>i & 1)pos = i;}int num1 = 0, num2 = 0;for(auto e : nums){if(e>>pos & 1)num1 ^= e;else num2 ^= e;}return {num1,num2};}
};

题目1: 判断字符是否为1

利用 位图 的思想, 每一个比特位代表一个字符, 一个 int 类型的变量的 32 位足够表示所有的小写字母. 比特位里面如果是 0, 表示这个字符没有出现过;比特位里面的值是 1 , 表示该字符出现过.
那么我们就可以用一个整数来充当哈希表

此外, 还可以利用鸽巢原理来做优化, 如果给定字符串长度大于26, 则一定会有一个重复字符.

class Solution {
public:bool isUnique(string astr) {if(astr.length() > 26)return false;int bitmap = 0;for(auto c : astr)if(((bitmap ^= 1<<c-97) & (1<<c-97)) == 0)return false;return true;}
};

题目2: 丢失的数字

此题和二分查找里的 一题"点名" 很像,  但是那道题是有序排列的数组, 能找到二段性从而利用二分查找去寻找缺失的值.

此题数组中的元素无序, 找不到二段性, 可以考虑用哈希表存储, 等差数列求和求解, 也可以用位运算异或求解, 先将0-n的数字异或, 再与nums中的每个元素异或, 结果就是缺失的数字.

class Solution {
public:int missingNumber(vector<int>& nums) {int ret = 0;for(auto e : nums)ret ^= e;for(int i = 0; i <= nums.size(); i++)ret ^= i;return ret;}
};

题目3: 两整数之和

最开始提到过, 异或是无进位的加法, 所以只要能找到进位, 就可以间接实现加法,  

以13 + 28 = 41为例, a=13,b=28, a^b的结果是无进位加法得到的结果, 而只有两个比特位都为1才会产生进位, 所以 a&b 就会得到产生进位的位置,  产生的进位需要加到下一位上, 所以需要(a&b)<<1, 这样才能对应进位要加到的位置上.

两者再进行异或相加, 再次计算进位的位置:

再进行一步异或相加, 发现不会再产生进位了, 那么加法就结束了, 最后一次 a^b 得到的结果就是最终加法的结果: 

class Solution {
public:int getSum(int a, int b) {int sum = a ^ b;int carry = (a & b) << 1;while(jinwei){int tmp = (sum & carry) << 1;sum = sum ^ jinwei;carry = tmp;}return sum;}
};

题目4: 只出现一次的数字2

 

考虑答案的第 i 个二进制位, 它可能为 0 或 1. 对于数组中非答案的元素, 每一个元素都出现了 3 次, 对应着第 i 个二进制位的 3 个 0 或 3 个 1, 无论是哪一种情况, 它们的1的数量出现的次数都是3n(n>=0), 因此: 答案第 i 个二进制位就是数组中所有元素的第 i 个二进制位 1 的次数之和以 3 的余数.

class Solution {
public:int singleNumber(vector<int>& nums) {int ret = 0;for(int i =0; i < 32; i++){int x = 0;for(auto e: nums){x += (e>>i) & 1;}ret |= (x % 3) << i;}return ret;}
};

题目5: 消失的两个数字

 本题可以转化为只出现一次的数字3, 求0~n中缺少的两个数字, 如果把1~n全部添加进数组中, 问题就转化为了:

做法和只出现一次的数组3一模一样了:

class Solution {
public:vector<int> missingTwo(vector<int>& nums) {int sum = 0, pos = 0, n = nums.size();//把1~n和nums里的数全部异或起来for(int i = 1; i <= n+2; i++)nums.push_back(i);for(auto e: nums)sum ^= e;//寻找最右边的1for(int i = 0; i < 32; i++){if(sum>>i & 1){pos = i;break;}}//分组异或int num1 = 0, num2 = 0;for(auto e: nums){if(e>>pos & 1)num1 ^= e;else num2 ^= e;}return {num1,num2};}
};

 


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

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

相关文章

软件工程-第11章 内容总结

如果不想读这本书&#xff0c;直接看这一章即可。 11.1 关于软件过程范型 11.2 关于软件设计方法

微信小程序开发学习笔记——4.4常见的导航栏api接口

>>跟着b站up主“咸虾米_”学习微信小程序开发中&#xff0c;把学习记录存到这方便后续查找。 课程连接&#xff1a;https://www.bilibili.com/video/BV19G4y1K74d?p29&vd_source9b149469177ab5fdc47515e14cf3cf74 一、属性 界面 / 导航栏 / wx.showNavigationBar…

Vue3+.NET6前后端分离式管理后台实战(四)

1&#xff0c;Vue3.NET6前后端分离式管理后台实战(四)已经发布&#xff0c; 程序源码已打包&#xff0c;感兴趣的可以关注下载。 2&#xff0c;源码打包可以下载&#xff1a;

leecode1793 | 好子数组的最大分数 | 求给高度矩阵最大值

题目我就不念了&#xff0c;就一个字难理解&#xff0c;给的题总是这么难懂&#xff0c;总感觉出题人的语文是体育老师教的&#xff1f; 还有就是思维转变&#xff0c;才能能好的理解&#xff1f;一味的钻牛角尖死理解&#xff0c;效果不好 思维的转变 >悟性&#xff1f;&am…

幼儿教育管理系统|基于jsp 技术+ Mysql+Java的幼儿教育管理系统设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java&#xff0c;ssm&#xff0c;springboot的平台设计与实现项目系统开发资源&#xff08;可…

深入理解Ubuntu22:探索Linux操作系统的功能与应用

一、linux &#xff08;一&#xff09;、安装 1、电脑可以安装双系统&#xff0c;即在一套硬件上只能同时运行一个操作系统&#xff0c;例&#xff1a;C盘安装win&#xff0c;D盘安装linux。 2、虚拟机 虚拟机需要硬件支持&#xff0c;并需开启VT-x. 如&#xff1a;Virtual…

气象ARWpost、grads 等使用的ctl和dat格式的grd 二进制文件

气象ARWpost、grads 和 Fortran使用的ctldat格式的grd 二进制文件&#xff0c;在Python中可以用xgrads包来读取或者转成nc 格式使用。 xgrads包官网说明地址&#xff1a;https://xgrads.readthedocs.io/en/latest/ 示例1&#xff0c;打开多个空间场相同&#xff0c;时间不同的 …

安防监控视频汇聚平台EasyCVR接入海康Ehome设备,设备在线但视频无法播放是什么原因?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

51单片机-蜂鸣器

1.蜂鸣器的介绍 无源蜂鸣器不能一直通电&#xff0c;无源蜂鸣器内部的线圈较小&#xff0c;易烧坏 蜂鸣器的驱动 达林顿晶体管&#xff08;npn型&#xff09; 应用&#xff1a; 按下独立按键同时蜂鸣器响起提示音&#xff0c;数码管显示对应的独立按键键码 #include <REG…

不确定性建模:传感器噪声与输入输出扰动/干扰

鲁棒性在控制系统设计中是至关重要的&#xff0c;因为实际的工程系统容易受到外部干扰和测量噪声的影响&#xff0c;而且在设计中使用的数学模型和实际中的实际系统之间总是存在差异。通常需要一个控制工程师设计一个控制器&#xff0c;使闭环系统稳定&#xff0c;并在存在干扰…

Spring的事务传播机制有哪些?

Spring的事务传播机制用于控制在多个事务方法相互调用时事务的行为。 一、问题解析 在复杂的业务场景中&#xff0c;多个事务方法之间的调用可能会导致事务的不一致&#xff0c;如出现数据丢失、重复提交等问题&#xff0c;使用事务传播机制可以避免这些问题的发生&#xff0c…

面试算法-66-二叉树的层序遍历

题目 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],[9,20],[15,7]] 解 class Solut…

HTML实现卷轴动画完整源码附注释

动画效果截图 页面的html结构代码 <!DOCTYPE html> <html> <head lang=

golang sync.Map之如何设计一个并发安全的读写分离结构?

在 golang中&#xff0c;想要并发安全的操作map&#xff0c;可以使用sync.Map结构&#xff0c;sync.Map 是一个适合读多写少的数据结构&#xff0c;今天我们来看看它的设计思想&#xff0c;来看看为什么说它适合读多写少的场景。 如下&#xff0c;是golang 中sync.Map的数据结构…

HarmonyOS ArkTS 通用事件(二十三)

通用事件目录 点击事件事件ClickEvent对象说明EventTarget8对象说明示例 触摸事件事件TouchEvent对象说明TouchObject对象说明示例 挂载卸载事件事件示例 点击事件 组件被点击时触发的事件。 事件 ClickEvent对象说明 从API version 9开始&#xff0c;该接口支持在ArkTS卡片中…

MySQL 多表关系(介绍) 一对多/多对多

一对多 举例介绍 例子: 部门与员工 在常理上来说: 一个部门有多个员工&#xff0c;一个员工只对应一个部门实现方式: 在多的一方建立外键&#xff0c;指向一的一方的主键 多对多 举例介绍 例子: 学生与课程 在常理上来说: 一个学生可以有多个课程,一门课程可以有多个学生实…

6.如何判断数据库搜索是否走索引?

判断是否使用索引搜索 索引在数据库中是一个不可或缺的存在&#xff0c;想让你的查询结果快准狠&#xff0c;还是需要索引的来帮忙&#xff0c;那么在mongo中如何判断搜索是不是走索引呢&#xff1f;通常使用执行计划&#xff08;解释计划、Explain Plan&#xff09;来查看查询…

【LEMONSQUEEZY: 1【mysql写shell】】

前期环境准备 靶机下载地址 https://vulnhub.com/entry/lemonsqueezy-1%2C473/ 信息收集 ┌──(root㉿kali)-[/home/test/桌面/lemmon] └─# nmap -sP 192.168.47.1/24 --min-rate 3333 Starting Nmap 7.92 ( https://nmap.org ) at 2024-03-20 14:02 CST Stats: 0:00:06 e…

关于layui如何动态更新数据

因为在写项目时用layui来完成后台&#xff0c;想点击下一页或者对于下拉切换数据的条数&#xff0c;然后一开始没注意它的table.render里面的url&#xff0c;&#xff08;不是没注意吧&#xff0c;就是没看到await等明显的请求方式&#xff0c;就以为它只能请求JSON文件里面的数…

ATG-2081功率信号源在电子实验中的应用

功率信号源被广泛应用于电子实验领域&#xff0c;主要用于产生精确、干净的高频信号。这些信号可以被用于测试各种电子器件和电路&#xff0c;例如射频、微波电路和天线等。下面将介绍功率信号源在电子实验中的应用。 功率信号源可以产生稳定、高质量的RF和微波信号&#xff0c…