算法训练之位运算

news2025/4/13 8:46:54


♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥

♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥

♥♥♥我们一起努力成为更好的自己~♥♥♥

♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥

♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥

✨✨✨✨✨✨ 个人主页✨✨✨✨✨✨

        在C语言阶段,我们学习了许多位运算操作符,具体可以看看这一篇博客C语言——操作符

接下来,我们就来使用这样一些操作符解决我们的算法问题~

目录

前置知识(常见操作)

1. 基础位运算

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

3. 将一个数n的二进制表示的第k位从0变成1,或从1变成0

将一个数n的二进制表示的第k位修改为1

将一个数n的二进制表示的第k位修改为0

4. 将一个数n的二进制表示的前k位改成1

5. 将一个数n的二进制表示的前k位改成0

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

7. 去掉一个数n的二进制表示中最右边的1

8. 位运算的优先级

9. 异或运算小技巧

只出现一次的数字

丢失的数字

两整数之和

只出现一次的数字Ⅱ

只出现一次的数据Ⅲ

消失的两个数字


前置知识(常见操作)

        在正式开始题目练习之前,除了前面博客里面说到的基础知识,我们还需要找到一些常见操作~


1. 基础位运算

  • 与运算 (&)(有0就是0)
    • 示例:0 & 0 = 0, 0 & 1 = 0, 1 & 0 = 0, 1 & 1 = 1
    • 解释:对应位都为1时结果为1,否则为0。
  • 或运算 (|)(有1就是1)
    • 示例:0 | 0 = 0, 0 | 1 = 1, 1 | 0 = 1, 1 | 1 = 1
    • 解释:对应位有一个为1时结果为1。
  • 异或运算 (^)(无进位相加)
    • 示例:0 ^ 0 = 0, 0 ^ 1 = 1, 1 ^ 0 = 1, 1 ^ 1 = 0
    • 解释:对应位不同结果为1,相同为0。
  • 取反运算 (~):
    • 示例:~0 = 1, ~1 = 0
    • 解释:0变1,1变0。

接下来,常见操作第k/x位都是指的是从后面往前面数的第k/x位

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

  • 方法:(n >> k) & 1
    • 解释:将n右移k位,然后与1进行与运算,判断最低位是0还是1。

举例:

3. 将一个数n的二进制表示的第k位从0变成1,或从1变成0

  • 方法:n = n ^ (1 << k)
    • 解释:将1左移k位,然后与n进行异或运算,翻转第k位。

举例:

将一个数n的二进制表示的第k位修改为1

        前面的操作就是直接变成相反的,如果我们现在想要把第k位修改为1,它可能最开始是1,就不需要变化了,如果最开始为0,我们把它变成1~

方法:n = n | (1 << k)

举例:

将一个数n的二进制表示的第k位修改为0

方法:n = n & (~(1<<x))

举例:

4. 将一个数n的二进制表示的前k位改成1

  • 方法:n = n | ((1 << k) - 1)
    • 解释:将1左移k位后减1,得到前k位全为1的数,然后与n进行或运算。

举例:

5. 将一个数n的二进制表示的前k位改成0

  • 方法:n = n & ~((1 << k) - 1)
    • 解释:将1左移k位后减1,取反后得到前k位全为0的数,然后与n进行与运算。

举例:

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

  • 方法:n & -nn & (~n + 1)
    • 解释:取反后加1得到补码,与原数进行与运算,提取最右边的1。

举例:

7. 去掉一个数n的二进制表示中最右边的1

  • 方法:n & (n - 1)
    • 解释:将n减1后与原数进行与运算,去掉最右边的1。

举例:

8. 位运算的优先级

  • 位运算的优先级低于算术运算,但高于逻辑运算。
  • 示例:a ^ b & c 等价于 a ^ (b & c)

技巧:不知道优先级,能加括号加括号

9. 异或运算小技巧

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

知道了这些常见操作,接下来我们就使用位运算来解决我们的算法问题~

只出现一次的数字

只出现一次的数字

这个题目就比较简单了~

思路:所有数据进行异或,根据a^a=0,a^0=a,可以得到所有数据进行异或的结果就是我们想要的数据~

代码:

class Solution 
{
public:
    int singleNumber(vector<int>& nums) 
    {
        int ret=0;
        //所有数据进行异或
        for(auto e:nums)
        {
            ret^=e;
        }
        return ret;
    }
};

顺利通过~

丢失的数字

丢失的数字

这个题目也比较简单,这些数是连续的,那么我们就可以利用数学里面的等差数列求和公式得到它本来的和,再减去已经存在的数据,就可以得到没有存在的数据~

思路1:等差数列求和

代码:

class Solution 
{
public:
    int missingNumber(vector<int>& nums) 
    {
        int n=nums.size();
        int ret=(1+n)*n/2;
        for(auto e:nums)
        {
            ret-=e;
        }
        return ret;
    }
};

思路2:使用位运算,得到0~n的数据,与原来的数组元素进行异或,结果就是原来数组没有存在的数据~

代码:

class Solution 
{
public:
    int missingNumber(vector<int>& nums) 
    {
        int ret=0;
        for(auto e:nums)
        {
            ret^=e;
        }
        //产生0~n的数据进行异或
        for(int i=0;i<=nums.size();i++)
        {
            ret^=i;
        }
        return ret;
    }
};

两整数之和

两整数之和

可能一看这个题目挺简单的,但是不可以使用+,-符号啊,不过有一种投机取巧的方法就是直接return a+b;

也是可以通过的,当然这肯定是不太正确的解法,接下来我们使用位运算来解决这个问题~

加法最重要的就是处理进位了,我们知道^是进行无进位相加,那如果我们想得到进位的数字呢?

简单,直接(a&b)<<1就可以得到进位(进位要往前面移动)了,我们来举例子看看:

思路:不断地进行a^b得到无进位相加的结果,(a&b)<<1得到进位的结果,直到没有进位就得到想要的结果~

代码:

class Solution 
{
public:
    int getSum(int a, int b) 
    {
        while(b)//没有进位就停止
        {
            int x=a^b;//无进位相加结果
            int y=(a&b)<<1;//进位结果
            a=x;
            b=y;//更新a、b
        }
        return a;
    }
};

顺利通过~

只出现一次的数字Ⅱ

只出现一次的数字Ⅱ

这个题目有点难度了,有一个数字出现了一次,其他数字出现了三次~

宏观上,我们不太好分析,我们来看看微观上,来看看一个整型的比特位,既然其他数字出现了三次,那么每一个比特位的结果可能是:

3n个0+1

3n个1+1

3n个0+0

3n个1+0

如果%3的结果不就是我们想要的出现一次的数字该比特位是0还是1吗?

思路:统计整型数组元素每一个比特位的和,和%3得到出现一次的数字该比特位,是1就进行修改~

代码:

class Solution 
{
public:
    int singleNumber(vector<int>& nums) 
    {
        int ret=0;
        //统计每一个比特位的和
        for(int i=0;i<32;i++)
        {
            int sum=0;
            for(auto e:nums)
            {
                if(((e>>i)&1)==1) 
                    sum++;//统计所有数据该比特位为1的个数
            }
            sum%=3;
            if(sum==1) //多的就是出现一次数字的
                ret|=(1<<i);//修改ret的该比特位为1
        }
        return ret;
    }
};

        事实上,这个题目不仅仅可以处理其他数据出现3次,还可以处理其他数据出现k次~方法都是一样的~

比如前面其他数据出现两次,只需要修改为sum%=2就可以了~

class Solution 
{
public:
    int singleNumber(vector<int>& nums) 
    {
        int ret=0;
        //统计每一个比特位的和
        for(int i=0;i<32;i++)
        {
            int sum=0;
            for(auto e:nums)
            {
                if(((e>>i)&1)==1) 
                    sum++;//统计所有数据该比特位为1的个数
            }
            sum%=2;
            if(sum==1) //多的就是出现一次数字的
                ret|=(1<<i);//修改ret的该比特位为1
        }
        return ret;
    }
};

只出现一次的数据Ⅲ

只出现一次的数据Ⅲ

这个题目有点难度,我们废话不多说,直接来思路:

思路:

A、将所有的数据进行异或,异或得到的结果就是那两个只出现一次的数据异或的结果

B、根据异或的结果找到某一个为1的比特位(根据异或的相同为0,相异为1),比特位为1,说明这两个数据这个比特位是不同的

C、根据找到的比特位把数据分为两组,一组该比特位为1进行异或,一组该比特位为0进行异或,因为其他因素出现了两次,两组得到的结果就是我们想要的结果

代码:

class Solution 
{
public:
    vector<int> singleNumber(vector<int>& nums) 
    {
        //1、异或所有的数
        int tmp=0;
        for(auto e:nums) tmp^=e;

        //tmp就是两个出现一次数异或的结果
        //2、找到为1的比特位
        int dicone=0;
        while(1)
        {
            if(((tmp>>dicone)&1)==1) break;
            else dicone++;
        }

        //3、根据dicone进行分组
        //既然dicone不同,两个不同的数字一定不会出现在一组
        int x=0,y=0;
        for(auto e:nums)
        {
            if(((e>>dicone)&1)==1) x^=e;
            else y^=e;
        }

        return {x,y};
    }
};

顺利通过~

消失的两个数字

消失的两个数字

有了前面的基础,相信这一道题目就比较简单了,事实上,与只出现一次的数字Ⅲ方法是类似的,如果我们再有1~N的数据,那么不就是两个数字出现一次,其他数据出现两次了吗~

思路:

A0:加上1~N的数据

A、将所有的数据进行异或,异或得到的结果就是那两个只出现一次的数据异或的结果

B、根据异或的结果找到某一个为1的比特位(根据异或的相同为0,相异为1),比特位为1,说明这两个数据这个比特位是不同的

C、根据找到的比特位把数据分为两组,一组该比特位为1进行异或,一组该比特位为0进行异或,因为其他因素出现了两次,两组得到的结果就是我们想要的结果

代码:

class Solution 
{
public:
    vector<int> missingTwo(vector<int>& nums) 
    {
        //1、所有的数据进行异或
        int ret=0;
        for(auto e:nums)
        {
            ret^=e;
        }
        //获取1~n的数据
        for(int n=1;n<=nums.size()+2;n++)
        {
            ret^=n;
        }

        //2、找到比特位为一的那一位
        int dicone=0;
        while(1)
        {
            if(((ret>>dicone)&1)==1) break;
            else dicone++;
        }

        //3、根据比特位分组异或
        int x=0,y=0;
        for(auto e:nums)
        {
            if(((e>>dicone)&1)==1) x^=e;
            else y^=e;
        }
        for(int n=1;n<=nums.size()+2;n++)
        {
            if(((n>>dicone)&1)==1) x^=n;
            else y^=n;
        }

        //4、返回结果
        return {x,y};//编译器会根据{}生成vector
    }
};

顺利通过~

        位运算具有高效性、简洁性、灵活性和性能优化等优势。它直接操作二进制位,执行速度快且资源消耗低,代码简洁易读,提供多种操作方式,适用于算法优化和底层硬件操作,是我们在编程中不可或缺的重要工具~所以合理正确的使用会给我们带来很大的方便~


♥♥♥本篇博客内容结束,期待与各位优秀程序员交流,有什么问题请私信♥♥♥

♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥

✨✨✨✨✨✨个人主页✨✨✨✨✨✨


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

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

相关文章

C++设计模式+异常处理

#include <iostream> #include <cstring> #include <cstdlib> #include <unistd.h> #include <sstream> #include <vector> #include <memory> #include <stdexcept> // 包含异常类using namespace std;// 该作业要求各位写一…

checkra1n越狱出现的USB error -10问题解决

使用checkra1n进行越狱是出现&#xff1a; 解决办法(使用命令行进行越狱)&#xff1a; 1. cd /Applications/checkra1n.app/Contents/MacOS 2. ./checkra1n -cv 3. 先进入恢复模式 a .可使用爱思助手 b. 或者长按home,出现关机的滑条&#xff0c;同时按住home和电源键&#…

golang-defer延迟机制

defer延迟机制 defer是什么 defer是go中一种延迟调用机制。 执行时机 defer后面的函数只有在当前函数执行完毕后才能执行。 执行顺序 将延迟的语句按defer的逆序进行执行&#xff0c;也就是说先被defer的语句最后被执行&#xff0c;最后被defer的语句&#xff0c;最先被执…

【小沐学Web3D】three.js 加载三维模型(Angular)

文章目录 1、简介1.1 three.js1.2 angular.js 2、three.js Angular.js结语 1、简介 1.1 three.js Three.js 是一款 webGL&#xff08;3D绘图标准&#xff09;引擎&#xff0c;可以运行于所有支持 webGL 的浏览器。Three.js 封装了 webGL 底层的 API &#xff0c;为我们提供了…

一种替代DOORS在WORD中进行需求管理的方法 (二)

一、前景 参考&#xff1a; 一种替代DOORS在WORD中进行需求管理的方法&#xff08;基于WORD插件的应用&#xff09;_doors aspice-CSDN博客 二、界面和资源 WORD2013/WORD2016 插件 【已使用该工具通过第三方功能安全产品认证】&#xff1a; 1、 核心功能 1、需求编号和跟…

一个基于ragflow的工业文档智能解析和问答系统

工业复杂文档解析系统 一个基于ragflow的工业文档智能解析和问答系统,支持多种文档格式的解析、知识库管理和智能问答功能。 系统功能 1. 文档管理 支持多种格式文档上传(PDF、Word、Excel、PPT、图片等)文档自动解析和分块处理实时处理进度显示文档解析结果预览批量文档…

23种设计模式-行为型模式-访问者

文章目录 简介场景解决完整代码核心实现 总结 简介 访问者是一种行为设计模式&#xff0c;它能把算法跟他所作用的对象隔离开来。 场景 假如你的团队开发了一款能够使用图像里地理信息的应用程序。图像中的每个节点既能代表复杂实体&#xff08;例如一座城市&#xff09;&am…

组播网络构建:IGMP、PIM 原理及应用实践

IP组播基础 组播基本架构 组播IP地址 一个组播IP地址并不是表示具体的某台主机&#xff0c;而是一组主机的集合&#xff0c;主机声明加入某组播组即标识自己需要接收目的地址为该组播地址的数据IP组播常见模型分为ASM模型和SSM模型ASM&#xff1a;成员接收任意源组播数据&…

建筑兔零基础自学记录69|爬虫Requests-2

Requests库初步尝试 #导入requests库 import requests #requests.get读取百度网页 rrequests.get(http://www.baidu.com) #输出读取网页状态 print(r.status_code) #输出网页源代码 print(r.text) HTTP 状态码是三位数字&#xff0c;用于表示 HTTP 请求的结果。常见的状态码有…

NVIDIA PhysX 和 Flow 现已完全开源

NVIDIA PhysX SDK 在 3-Clause BSD 许可下开源已有六年半了&#xff0c;但其中并非所有内容都是开源的。直到最近&#xff0c;随着 GPU 模拟内核源代码在 GitHub 上的发布&#xff0c;这种情况才有所改变。以下是 NVIDIA 分享的消息&#xff0c;以及 Flow SDK 着色器实现的发布…

电脑DNS出错无法打开网页

目录 解决步骤 打开“控制面板”--》“查看网络状态和任务” 打开“更改适配器设置” 对WLAN右键&#xff0c;打开属性 打开“使用下面的DNS服务器地址”--》高级 添加“114.114.114.114”&#xff0c;点击确定 今天晚上突然网页打不开了&#xff0c;一开始我以为是网络的…

[Redis]redis-windows下载安装与使用

本篇记录windows redis下载安装与使用。 下载 官网下载方式(没windows版) https://redis.io/downloads/#stack 可以选择下载社区版Redis CE与增强版Redis Stack。 两者都不支持直接运行在windows上&#xff0c;需要Docker环境。 You can install Redis CE locally on your …

极氪汽车云原生架构落地实践

云原生架构落地实践的背景 随着极氪数字业务的飞速发展&#xff0c;背后的 IT 技术也在不断更新迭代。极氪极为重视客户对服务的体验&#xff0c;并将系统稳定性、业务功能的迭代效率、问题的快速定位和解决视为构建核心竞争力的基石。 为快速响应用户的需求&#xff0c;例如…

2025年AI开发学习路线

目录 一、基础阶段&#xff08;2-3个月&#xff09; 1. 数学与编程基础 2. 机器学习入门 二、核心技能&#xff08;3-4个月&#xff09; 1. 深度学习与框架 2. 大模型开发&#xff08;重点&#xff09; 三、进阶方向&#xff08;3-6个月&#xff09; 1. 多模态与智能体…

oracle 动态性能视图

Oracle 数据库中的 V$SQLAREA 是一个动态性能视图&#xff08;Dynamic Performance View&#xff09;&#xff0c;用于记录共享池&#xff08;Shared Pool&#xff09;中所有 SQL 语句的统计信息。每个 SQL 语句在共享池中存储为一个游标&#xff08;Cursor&#xff09;&#x…

Vue3+Vite+TypeScript+Element Plus开发-10.多用户动态加载菜单

系列文档目录 Vue3ViteTypeScript安装 Element Plus安装与配置 主页设计与router配置 静态菜单设计 Pinia引入 Header响应式菜单缩展 Mockjs引用与Axios封装 登录设计 登录成功跳转主页 多用户动态加载菜单 Pinia持久化 动态路由-配置 文章目录 目录 系列文档目…

前端用户列表与后端分页协同设计

分页实现方案 在现代Web应用中&#xff0c;用户列表展示与分页是一个常见的功能需求。前端与后端通过API协同工作&#xff0c;使用PageHelper等工具实现高效分页。 例如&#xff1a; 后端实现 (使用PageHelper) public PageResult DishPage(DishPageQueryDTO dishPageQuery…

精准测试建设过程中遇到的一些问题

1.sqlite3 仅可以处理单个任务问题&#xff0c;多线程往往会面临数据库锁定 因为仅临时存储&#xff0c;后来在创建数据库时&#xff0c;给每个任务开了一个临时数据库&#xff0c;存储数据执行完毕后&#xff0c;删除db sql_insert_new:INSERT INTO analyze_api_resault_dynam…

【Docker】Dockerfile 编写实践

&#x1f47b;创作者&#xff1a;丶重明 &#x1f47b;创作时间&#xff1a;2025年4月8日 &#x1f47b;擅长领域&#xff1a;运维 目录 1. Dockerfile编写原则1.1.选择合适的基础镜像1.2.镜像层优化1.3.多阶段构建1.4.安全增强 2. 关键指令与技巧2.1.COPY vs ADD2.2.ENTRYPOIN…

LabVIEW商业软件开发注意问题

在 LabVIEW 商业软件开发进程中&#xff0c;性能优化、界面设计及兼容性与扩展性&#xff0c;对软件品质、用户体验和市场适配性起着决定性作用。下面&#xff0c;借助多个LabVIEW 编程特性的实际案例&#xff0c;深入分析这些方面的开发要点。 一、性能优化&#xff1a;提升软…