【算法】位运算

news2024/9/20 16:42:49

【ps】本篇有 10 道 leetcode OJ。 

目录

一、算法简介

二、相关例题

1)位1的个数

.1- 题目解析

.2- 代码编写

2)比特位计数

.1- 题目解析

.2- 代码编写

3)汉明距离

.1- 题目解析

.2- 代码编写

4)只出现一次的数字

.1- 题目解析

.2- 代码编写

5)只出现一次的数字 III

.1- 题目解析

.2- 代码编写

6)判定字符是否唯一

.1- 题目解析

.2- 代码编写

7)丢失的数字

.1- 题目解析

.2- 代码编写

8)两整数之和

.1- 题目解析

.2- 代码编写

9)只出现一次的数字 II

.1- 题目解析

.2- 代码编写

10)消失的两个数字

.1- 题目解析

.2- 代码编写


 

一、算法简介

        程序中的所有数在计算机内存中都是以二进制的形式储存的,位运算就是直接对整数在内存中的二进制位进行操作,常见有逻辑与(&)、逻辑或(|)、逻辑异或(^)、按位取反(~)等。

【Tips】常见位运算方法总结

(1)区分位运算的优先级:别人工区分了,按题目要求能加括号就加括号!

(2)基础的与、或、异或

(3)位图相关

        确定 n 的二进制数的第 x 位是 0 还是 1:右移 x 后与 1

        将 n 的二进制数的第 x 位修改成 1:或上 1 的左移 x

        将 n 的二进制数的第 x 位修改成 0:与上取反的 1 的左移 x

        提取 n 的二进制数最右侧的 1:n 与 -n

        删除 n 的二进制数最右侧的 1:n 与 n - 1

        位图(哈希表)标识二进制数的比特位

(4)异或运算律

二、相关例题

1)位1的个数

191. 位1的个数

.1- 题目解析

        可以依此删除二进制数最右侧的 1,每删除一次都用一个计数器对其进行统计,直到二进制数中的 1 全部被删除。

.2- 代码编写

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

2)比特位计数

338. 比特位计数

.1- 题目解析

        这道题只是从上道题的“对一个二进制数求位 1 的个数”,变成了“对多个二进制数求位 1 的个数”。

.2- 代码编写

class Solution {
public:
    vector<int> countBits(int n) {
        vector<int> v(n+1, 0);
        for(int i = 1; i <= n; ++i)
        {
            int cnt = 0, tmp = i;
            while(tmp)
            {
                tmp &= (tmp - 1);
                ++cnt;
            }
            v[i] = cnt;
        }
        return v;
    }
};

3)汉明距离

461. 汉明距离

.1- 题目解析

        将两个数异或后,两个数相同的比特位会变成 0 ,不同的比特位会变成 1,此时只需统计异或结果的 1 的个数即可。

.2- 代码编写

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

4)只出现一次的数字

136. 只出现一次的数字

.1- 题目解析

        把所有的数异或到一起,相同的数异或后结果为 0 ,剩余那个只出现了一次的数异或 0 还会等于它本身。

.2- 代码编写

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

5)只出现一次的数字 III

260. 只出现一次的数字 III

.1- 题目解析

       假设数组中只有 2 和 4 只出现过一次。将数组中所有数字异或到一起,其他出现两次的数就抵消了,此时相当于 2 异或 4,即 0010 ^ 0100 =  0110.

        结合异或的结果和异或前运算数的比特位不难发现,只出现一次的数,其比特位在异或的结果中会被标识出来。由此,我们可以取异或结果最右侧的 1 是在第 cnt 个比特位上,将 1 左移 cnt 个比特位就能得到原来的数,此时再将 1 左移 cnt 个比特位的结果与数组异或一遍,以验证结果是否在数组中存在,若存在即返回该结果。

.2- 代码编写

class Solution {
public:
	vector<int> singleNumber(vector<int>& nums) {
        //1.将所有数异或到一起
		int sum = 0;
		for (const auto& e : nums)
		{
			sum ^= e;
		}
        //2.找到异或结果最右侧的 1 是第几个比特位
		int cnt = 0;
		for (int i = 0;i < 32;++i)
		{
			if (sum & (1 << i)) // 第几位是1,&的结果就是1
			{
				cnt = i;
			}
		}
        //3.在原数组中验证并返回结果
		vector<int> v = { 0,0 }; // C++11的初始化方式
		for (const auto& e : nums)
		{
			if (e & (1 << cnt)) // 第几位是1,&的结果就是1
			{
				v[0] ^= e; //记录其中一个只出现一次的数
			}
			else
			{
				v[1] ^= e; //另一个数只与其他出现过两次的数异或在一起,就得到了另一个数
			}
		}
		return v;
	}
};
//虽然吧,这道题用哈希的方式显然更方便,但本篇的专题是位运算呐!
class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        unordered_map<int, int> freq;
        for (auto e: nums) {
            ++freq[e];
        }
        vector<int> ans;
        for (const auto& [e, occ]: freq) {
            if (occ == 1) {
                ans.push_back(e);
            }
        }
        return ans;
    }
};

6)判定字符是否唯一

面试题 01.01. 判定字符是否唯一

.1- 题目解析

        不难想到用一个哈希表来统计每个字符出现的频次,但其实我们还可以用一个位图来充当哈希表。

        一个整型变量有 32 个比特位,每个比特位上的值要么为 0 ,要么为 1。而所谓位图,就是用一个 32 位的整型变量来对某些情况进行标识,其中,相应比特位上的值为 0 则表示不存在,为 1 则表示存在。

        此外,我们还可以对位图标识字符是否出现的这个过程,用鸽巢原理进行优化。

【Tips】 鸽巢原理

        若存在 n + 1 只鸽子,n 个巢穴,则至少有一个巢穴中的鸽子数量大于 1。

.2- 代码编写

class Solution {
public:
    bool isUnique(string astr) {
        if(astr.size()>26) //鸽巢原理优化
             return false;
        int bitMap=0;
        for(auto ch:astr)
        {
            int i=ch-'a';
            if((bitMap>>i)&1==1) //如果一个字符已经标识过了,就说明该字符是不唯一的
                return false;
            bitMap|=1<<i;        //标识尚未被标识的字符
        }
        return true; //位图能够顺利统计完所有字符,就说明字符串的所有字符是唯一的
    }
};

7)丢失的数字

268. 丢失的数字

.1- 题目解析

        直接将 [ 0,n ] 中的所有数,和数组中所有数异或在一起,就能找到那个不存在的数了。

.2- 代码编写

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int n=nums.size();
        int ret=0;
        //异或数组中的数
        for(auto e:nums)
        {
            ret^=e;
        }
        //异或[ 0,n ] 中的数
        for(int i=0;i<=n;i++)
        {
            ret^=i;
        }
        return ret;
    }
};

8)两整数之和

371. 两整数之和

.1- 题目解析

        如果是在笔试场上,可以不讲武德,直接 return a + b 即可。

        不过,这道题同样可以用异或运算律来解决。

        将两个二进制数与在一起的结果左移 1 位,会将它们相加时进位的比特位给标识出来。而两个数异或在一起,能够得到它们相加时未进位的部分,此时只需将左移 1 位的与结果和异或结果再异或一次,就能够得到两个数的和了。

         设 a = 13,b = 28,则 a + b = 41,我们只需对 a 和 b 重复上述过程,就能得到它们的和 41。

 

.2- 代码编写

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

9)只出现一次的数字 II

137. 只出现一次的数字 II

 

.1- 题目解析

        若一个数只在数组中出现一次,其它数恰出现三次,则对于数组中所有的数,将它们的任意一个比特位统一地求和,都可能出现如下几种情况:

        如果将每一个比特位的这些情况 % 3,就能得到只出现一次的数的每一个比特位。

 

.2- 代码编写

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ret = 0;//储存结果
        for(int i = 0; i < 32; ++i) // 依次修改ret的32个比特位(0改1)
            int sum = 0;
            for(auto e : nums)
                if(((e >> i) & 1) == 1) //计算nums中所有数第i个比特位之和
                     ++sum; 
            sum %= 3;
            if(sum == 1) 
                ret |= (1 << i); // 将ret的第i位改为1           
        return ret;
    }
};

10)消失的两个数字

面试题 17.19. 消失的两个数字

 

.1- 题目解析

        把这消失的两个数看成一个整体,然后将数组中的所有数与 [ 0,n ] 中的所有数异或在一起,就能得到这个整体。此时这个整体也是两个数异或的结果,这个结果中存在为 1 的比特位,就说明该比特位在两个数中必有一个为 1。

        由此,我们可以将数组中的数分为两类,一类是该比特位上为 1 的数,另一类是该比特位上不为 1 的数。然后再将这两类数分别异或在一起,就能分别得到消失的两个数了。

 

.2- 代码编写

class Solution {
public:
    vector<int> missingTwo(vector<int>& nums) {
        //1.将所有数异或在一起
        int tmp=0;
        for(auto e:nums)tmp^=e;
        for(int i=1;i<=nums.size()+2;i++)
            tmp^=i;
        //2.找出异或结果中为1的比特位
        int diff=0;
        while(1)
        {
            if(((tmp>>diff)&1)==1)
                break;
            diff++;
        }
        //3.将所有数分为两类,分别异或得出两个消失的数
        int a=0,b=0;
        for(auto e:nums)
        {
            if(((e>>diff)&1)==1) 
                a^=e;
            else 
                b^=e;
        }
        for(int i=1;i<=nums.size()+2;i++)
        {
            if(((i>>diff)&1)==1) 
                a^=i;
            else 
                b^=i;
        }
        return {a,b};
    }
};

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

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

相关文章

3000字带你了解SD提示词用法,一点就通,小白轻松上手(附提示词生成器)(1.4 SD提示词运用)

提示词是什么 提示词是我们向AI模型发出的指令。正确的提示词能让AI准确反馈所需的输出&#xff0c;而优质的提示词则能使AI生成的内容更优质、更符合你的期望。这与编写程序代码颇为相似&#xff0c;准确的代码逻辑是程序正常运行的前提&#xff0c;而优秀的代码则能减少运行…

知识付费小程序源码轻松实现一站式运营,开启知识变现之旅

技术栈&#xff1a; 以下是一个简单的知识付费小程序的示例代码&#xff1a; app.js&#xff1a;小程序的入口文件 App({onLaunch: function () {// 在小程序启动时执行的代码},globalData: {// 存储全局数据userInfo: null // 用户信息} })pages/index/index.js&#xff1…

【学术会议征稿】第四届智能电网与能源互联网国际会议(SGEI 2024)

第四届智能电网与能源互联网国际会议&#xff08;SGEI 2024&#xff09; 2024 4th International Conference on Smart Grid and Energy Internet 为交流近年来国内外在智能电网和能源互联网领域的理论、技术和应用的最新进展&#xff0c;展示最新成果&#xff0c;由沈阳工业…

Visual Studio 2022 下载和安装

文章目录 概述一&#xff0c;下载步骤二&#xff0c;安装过程 概述 Visual Studio 提供 AI 增强功能&#xff0c;例如用于上下文感知代码补全的 IntelliSense 和可利用开源代码中的 AI 模式的 IntelliCode。 集成的 GitHub Copilot 提供 AI 支持的代码补全、聊天辅助、调试建议…

ElasticSearch学习笔记(三)RestClient操作文档、DSL查询文档、搜索结果排序

文章目录 前言5 RestClient操作文档5.4 删除文档5.4 修改文档5.5 批量导入文档 6 DSL查询文档6.1 准备工作6.2 全文检索查询6.3 精准查询6.4 地理坐标查询6.5 复合查询6.5.1 相关性算分6.5.2 布尔查询 7 搜索结果处理7.1 排序7.1.1 普通字段排序7.1.2 地理坐标排序 前言 Elast…

qmt量化交易策略小白学习笔记第59期【qmt编程之期权数据--获取指定期权品种的详细信息--原生Python】

qmt编程之获取期权数据 qmt更加详细的教程方法&#xff0c;会持续慢慢梳理。 也可找寻博主的历史文章&#xff0c;搜索关键词查看解决方案 &#xff01; 基于BS模型计算欧式期权理论价格 基于Black-Scholes-Merton模型&#xff0c;输入期权标的价格、期权行权价、无风险利率…

Mac 安装Hadoop教程(HomeBrew安装)

1. 引言 本教程旨在介绍在Mac 电脑上安装Hadoop&#xff0c;便于编程开发人员对大数据技术的熟悉和掌握。 2.前提条件 2.1 安装JDK 想要在你的Mac电脑上安装Hadoop&#xff0c;你必须首先安装JDK。具体安装步骤这里就不详细描述了。你可参考Mac 安装JDK8。 2.2 配置ssh环境…

从腰子的营养成分来分析腰子能否“壮阳”,健康地吃腰子。

文章目录 引言I 腰子的营养优点缺点吃腰子无“壮阳”效果II 健康地吃腰子食用前充分清洗浸泡高尿酸及痛风群体慎吃适量吃引言 很多人认为动物内脏有着“以形补形”的好处,如吃动物腰子,能补肾、壮阳,这让很多人对“腰子”非常热爱。 腰子的营养到底如何?经常吃腰子对身体…

优思学院:FMEA与FTA故障树方法对比:工程师必须知道的关键点!

故障树分析&#xff08;Fault Tree Analysis, FTA&#xff09;以某个不希望发生的产品故障事件或严重的系统风险&#xff08;即顶事件&#xff09;为分析对象&#xff0c;通过自上而下的分层次因果逻辑分析&#xff0c;逐步找出导致故障事件的必要且充分的直接原因&#xff0c;…

日程安排组件DHTMLX Scheduler v7.1 - 支持RFC-5545格式

DHTMLX Scheduler是一个类似于Google日历的JavaScript日程安排控件&#xff0c;日历事件通过Ajax动态加载&#xff0c;支持通过拖放功能调整事件日期和时间&#xff0c;事件可以按天、周、月三个种视图显示。 此版本包括几个备受期待的特性&#xff0c;可以帮助用户增强DHTMLX…

基于php+vue+uniapp的医院预约挂号系统小程序

开发语言&#xff1a;PHP框架&#xff1a;phpuniapp数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;PhpStorm 系统展示 后台登录界面 管理员功能界面 用户管理 医生管理 科室分类管理 医生信息管理 预…

30s到0.8s,记录一次接口优化成功案例!

大家好&#xff0c;我是沐子&#xff0c;推荐一个程序员免费学习的编程网站 我爱编程网&#xff08;www.love-coding.com&#xff09; ** 场景** 在高并发的数据处理场景中&#xff0c;接口响应时间的优化显得尤为重要。本文将分享一个真实案例&#xff0c;其中一个数据量达到…

(5) 归并排序

归并排序 归并排序是一种分治策略的排序算法。它是一种比较特殊的排序算法&#xff0c;通过递归地先使每个子序列有序&#xff0c;再将两个有序的序列进行合并成一个有序的序列。 归并排序首先由著名的现代计算机之父 John_von_Neumann 在 1945 年发明&#xff0c;被用在了 E…

【Python】Python 读取Excel、DataFrame对比并选出差异数据,重新写入Excel

背景&#xff1a;我在2个系统下载出了两个Excel&#xff0c;现在通过对下载的2个Excel数据&#xff0c;并选出差异数据 从新写入一个新的Excel中 differences_url rC:\Users\LENOVO\Downloads\differences.xlsx; //要生成的差异Excel的位置及名称 df1_url rC:\Users\LENOVO\Dow…

终于知道如何简化时间序列的特征工程了!

在处理时间序列数据时&#xff0c;时间特征往往是最基础且独特的要素&#xff0c;我们的目标通常是预测某种未来的响应或结果。 不过在很多情况下&#xff0c;除了时间特征之外&#xff0c;我们还能获取到一系列其他相关的特征或变量。 时间序列数据中的特征工程涉及从原始时…

进程、线程、时间片

1、操作系统中的程序&#xff08;如微信&#xff09;在运行时&#xff0c;系统会产生一个或多个进程&#xff0c;往往是一个 2、进程内可以包含多个线程&#xff0c;有一个主线程&#xff0c;主线程结束时&#xff0c;进程结束&#xff0c;进而程序结束 3、线程是cpu调度执行…

sql日期函数

目录 sql日期函数 1.获取日期时间函数 1.1 获取当前日期时间 1.2 获取当前日期 1.3 获取当前时间 2.datetime数据类型格式化 3.字符串数据类型转换成datetime数据类型 4.增加和减少时间间隔 5. 日期相差天数&#xff08;天&#xff09; 6. 相差时间&#xff08;小时&am…

GitHub Star 数量前 11 的开源内部工具

欢迎回到我们的 GitHub Star 系列文章&#xff01; 在之前的文章中&#xff0c;我们深入探讨了 GitHub 上最受欢迎的开源低代码项目《GitHub Star 数量前 15 的开源低代码项目》和开源无代码工具《GitHub Star 数量前 12 的开源无代码工具》&#xff0c;获得了热烈的反馈。本周…

【嵌入式学习笔记】---- OLED屏幕工作原理

1 驱动芯片SSD1603简介 1.1 SSD1603芯片图 SSD1603是一款点阵显示屏控制器&#xff0c;可嵌入在屏幕中&#xff0c;用于执行接收数据、显示存储、扫描刷新等任务驱动接口&#xff1a;128个SEG引脚和64个COM引脚&#xff0c;对应 128 64 128\times 64 12864像素点阵显示屏内置…

增强RAG:选择最佳的嵌入和重排模型

对于如何选择最佳的嵌入模型和重排模型&#xff0c;给出了详细的步骤和代码。 在构建检索增强生成&#xff08;RAG&#xff09;管道时&#xff0c;关键组件之一是检索器。我们有多种嵌入模型可供选择&#xff0c;包括 OpenAI、CohereAI 和开源的sentence transformers。此外&a…