【二十一】【算法分析与设计】位运算(2)

137. 只出现一次的数字 II

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。

你必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题。

示例 1:

输入:nums = [2,2,3,2] 输出:3

示例 2:

输入:nums = [0,1,0,1,0,1,99] 输出:99

提示:

  • 1 <= nums.length <= 3 * 10(4)

  • -2(31) <= nums[i] <= 2(31)1

  • nums 中,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次

我们主要关注一个数的二进制数。如果某个元素出现了三次,那么这个元素的二进制数也将出现三次。

如果我们把所有元素的二进制数各位都累加起来,比如说二进制数第0位,一共有多少个1,第二位有多少个1,以此类推。

再依次对3取余,只出现一次的二进制数最终会得到1,出现了三次的二进制数会得到0。

再依次把对应的二进制数移到一个整数上即可。

 
class Solution {
public:int singleNumber(vector<int>& nums) {vector<int> sum(32);for (int i = 0; i < nums.size(); i++)for (int j = 0; j < 32; j++)sum[j] = (sum[j] + ((nums[i] >> j) & 1)) % 3;int ret = 0;for (int i = 0; i < sum.size(); i++)if (sum[i] == 1)ret |= 1 << i;return ret;}
};

vector<int> sum(32);

sum(32)初始化了一个大小为32的向量,所有元素默认为0,用于统计每个位上的1的个数。

接下来,使用两层循环遍历数组nums中的每个元素,以及这些元素的每一位。外层循环遍历数组中的每个元素,内层循环遍历这个元素二进制的每一位。

for (int i = 0; i < nums.size(); i++) for (int j = 0; j < 32; j++)

nums.size()获取数组nums的大小,即元素的数量。

for (int j = 0; j < 32; j++)则是对每个元素二进制的每一位进行遍历。

在内层循环内部,使用位运算来检查每一位上的值(0或1),并更新sum向量。

sum[j] = (sum[j] + ((nums[i] >> j) & 1)) % 3;

(nums[i] >> j) & 1计算nums[i]的第j位是0还是1。sum[j] + ...将这个值加到sum[j]上,% 3确保如果同一位上的1出现了三次,就重置为0。

接下来,定义了一个名为ret的整型变量,并初始化为0。这个变量用于存储最终结果。

int ret = 0;

再次遍历sum向量,这次是为了重构出只出现一次的那个数。如果sum[i]等于1,意味着该位在只出现一次的数字中是1,所以将这一位设置为1。

for (int i = 0; i < sum.size(); i++) if (sum[i] == 1) ret |= 1 << i;

if (sum[i] == 1)检查每位上的计数是否为1,如果是,则通过ret |= 1 << i;将结果中的对应位设置为1。

这段代码的时间复杂度是O(n),因为它需要遍历数组中的每个元素两次(一次计算每位上1的个数,一次重建结果)。

空间复杂度是O(1),因为使用的额外空间(sum向量)的大小是固定的,与输入数组的大小无关。

另一种写法:

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

在函数内部,首先定义了一个名为ret的整型变量,并初始化为0。这个变量将用于存储和返回最终的结果。

int ret = 0;

接下来,代码进入一个循环,遍历整数的每一位(从0到31位)。这是因为在32位整数中,每个整数都可以用32位二进制数表示。遍历每一位的意义是维护ret的每一位。

for (int i = 0; i < 32; i++) {

在这个循环内部,首先定义了一个名为sum的整型变量,并初始化为0。这个变量用于累加数组nums中所有元素的第i位上的值。

int sum = 0;

然后,通过一个范围for循环遍历数组nums中的每个元素,对每个元素执行位运算,将第i位上的值加到sum变量上。

for (auto x : nums) sum += (x >> i) & 1;

这里,(x >> i) & 1是位运算的组合。x >> i将元素x右移i位,将第i位移动到最低位;然后与1进行按位与操作(&),以获取该位的值(0或1)。

接下来,sum变量对3取模,这是因为题目说明了每个元素都出现三次除了一个元素之外,所以任何出现三次的位贡献都将被消除。

sum %= 3;

如果sum的结果是1,这意味着在这个特定的位上,只出现一次的元素贡献了一个1。因此,需要将这个位的贡献加到ret变量上。

if (sum == 1) ret |= 1 << i;

这里,1 << i是将1左移i位,创建一个在第i位上有1的数,然后使用按位或操作(|=),将这个位的贡献合并到ret中。

最后,函数返回ret变量,即那个只出现一次的元素的值。

这段代码的时间复杂度是O(n),其中n是数组nums的长度。这是因为它需要遍历数组两次:一次是外部的32次迭代(对于每个位),另一次是内部循环遍历所有元素。

空间复杂度是O(1),因为使用的额外空间不依赖于输入数组的大小。

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

给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字。你能在 O(N) 时间内只用 O(1) 的空间找到它们吗?

以任意顺序返回这两个数字均可。

示例 1:

 

输入: [1]输出: [2,3]

示例 2:

 

输入: [2,3]输出: [1,4]

提示:

  • nums.length <= 30000

异或操作的性质:

a^0=a

a^a=0

a^b^c=a^(b^c)

n=nums.size()n表示数组的长度,很容易发现没有丢失数字的原数组范围应该是[1,n+2]

如果将原数组和现数组全部异或在一起,最后的结果是丢失的两个数字的异或结果。

异或的含义是,相同为0,不同为1。

丢失的两个数字一定是不同的,所以一定有1的存在,假设是第x位,意味着丢失的数字a第x位是1,丢失的数字b第x位是0,这就是划分的标准。

而我们要将这两个数字划分出来,就可以将原数组和现数组划分成两组。一组是第x位是1的一组,另一组是第x位是0的一组。

此时a和b被分为两组,每一组只有一个丢失的数字,分别对两组全部异或在一起,得到的结果就是两个丢失的数字。

 
class Solution {
public:vector<int> missingTwo(vector<int>& nums) {int n = nums.size();int ret = 0;for (const auto& x : nums)ret ^= x;for (int i = 1; i <= n + 2; i++)ret ^= i;int x = 0;for (int i = 0; i < 32; i++)if (((ret >> i) & 1) == 1) {x = i;break;}int a = 0, b = 0;for (const auto& x1 : nums)if (((x1 >> x) & 1) == 1)a ^= x1;elseb ^= x1;for (int i = 1; i <= n + 2; i++)if (((i >> x) & 1) == 1)a ^= i;elseb ^= i;return {a, b};}
};

int n = nums.size(); int ret = 0;

n记录了数组nums的大小。ret用于累积异或结果。

然后,遍历数组nums,对每个元素执行异或操作,将结果累积到ret中。接下来,继续通过异或操作将从1到n+2的所有数字累积到ret中。由于异或操作的性质,两次遍历后ret中存储的就是两个缺失数字的异或结果。

for (const auto& x : nums) ret ^= x; for (int i = 1; i <= n + 2; i++) ret ^= i;

异或操作符^=用于计算两个数字的异或。

接着,寻找ret结果中为1的最低位,这个位将用于区分两个缺失数字。找到这个位后,将其索引存储在变量x中。

int x = 0; for (int i = 0; i < 32; i++) if (((ret >> i) & 1) == 1) { x = i; break; }

这段代码通过位移和位与操作找到ret中第一个为1的位。

然后,定义了两个整型变量ab,初始化为0。这两个变量将分别存储两个缺失数字。通过再次遍历数组nums和从1到n+2的所有数字,根据在x位的值(0或1),将数字分成两组,并分别对每组进行异或操作。这样,最终ab中就分别存储了两个缺失的数字。

int a = 0, b = 0; for (const auto& x1 : nums) if (((x1 >> x) & 1) == 1) a ^= x1; else b ^= x1; for (int i = 1; i <= n + 2; i++) if (((i >> x) & 1) == 1) a ^= i; else b ^= i;

通过if (((x1 >> x) & 1) == 1)判断每个数在x位的值,并根据这个值分组异或,最终找到缺失的两个数字。

最后,函数通过创建一个包含ab的向量来返回这两个缺失的数字。

这段代码的时间复杂度为O(n),因为它需要遍历输入数组两次以及1到n+2的范围两次。

空间复杂度为O(1),因为除了输入数组外,只使用了常数额外空间。

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!

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

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

相关文章

Web安全基础入门+信息收集篇

教程介绍 学习信息收集&#xff0c;针对域名信息,解析信息,网站信息,服务器信息等&#xff1b;学习端口扫描&#xff0c;针对端口进行服务探针,理解服务及端口对应关系&#xff1b;学习WEB扫描&#xff0c;主要针对敏感文件,安全漏洞,子域名信息等&#xff1b;学习信息收集方法…

SpringMVC | SpringMVC中的 “文件上传和下载”

目录: 一、文件上传1.1 文件上传“概述”1.2 文件上传“具体配置” :“前端”中配置“文件上传” ( type“file” 满足3个条件 )“后端”中配置“文件上传” ( 配置id为“CommonsMultipartResolver”的bean 配置“文件上传”的“约束条件” 通过“MultipartFile接口”参数接…

自然语言处理: 第十八章微调技术之QLoRA

文章地址: QLoRA: Efficient Finetuning of Quantized LLMs (arxiv.org) 项目地址: artidoro/qlora: QLoRA: Efficient Finetuning of Quantized LLMs (github.com) 前言 QLoRA是来自华盛顿大学的Tim Dettmers大神提出的模型量化算法&#xff0c;应用于LLM训练&#xff0c;降…

007、IronPython与.NET Framework

今天就随便找个话题——聊聊IronPython与.NET Framework吧&#xff0c;算了&#xff0c;这东西比较专业&#xff0c;为避免说错话&#xff0c;还是从网上搜点专业术语贴出来吧&#xff0c;希望对学习Dynamo的小伙伴能有些帮助。 IronPython IronPython只是Python语言的一种方…

【Linux基础】dash和bash简介

Dash&#xff08;Debian Almquist Shell&#xff09;和 Bash&#xff08;Bourne Again Shell&#xff09;是 Unix/Linux 系统中的命令行 shell&#xff0c;用于执行命令、管理文件系统、编写脚本等任务的工具。 一、Dash和Bash的区别&#xff1a; Bash&#xff08;Bourne Agai…

RabbitMQ 的高阶应用及可靠性保证

目录 一、RabbitMQ 高阶应用 1.1 消息何去何从 1.2 过期时间 1.3 死信队列 1.4 延迟队列 1.5 优先级队列 1.6 消费质量保证&#xff08;QOS&#xff09; 二、持久化 三、生产者确认 四、消息可靠性和重复消费 4.1 消息可靠性 4.2 重复消费问题 上篇文章介绍了 Rabb…

前端打印(不使用插件,没有副作用)

今天要写一个打印,以前写过是直接调用window.print() ,因为之间是点击预览,跳转页面,打印,所以也没发现有什么副作用,今天,打印是在当前页打印局部,网上部分方法说,打印前把body赋值成局部元素,打印后复原,我发现这样会让打印之后,页面点击事件失效 但是我找到一个无副作用的,…

流畅的 Python 第二版(GPT 重译)(三)

第五章&#xff1a;数据类构建器 数据类就像孩子一样。它们作为一个起点是可以的&#xff0c;但要作为一个成熟的对象参与&#xff0c;它们需要承担一些责任。 马丁福勒和肯特贝克 Python 提供了几种构建简单类的方法&#xff0c;这些类只是一组字段&#xff0c;几乎没有额外功…

前端vue2学习(事件处理)总结

事件处理 <div id"root"><h2>事件处理&#xff0c;hello&#xff0c;{{name}}</hr><button v-on:click"showInfo1">点我提示信息1(不传参)</botton><button click"showInfo2(12,$event)">点我提示信息2&am…

Linux 安装 JDK、MySQL、Tomcat(图文并茂)

所需资料 下载 1.1 软件安装方式 在Linux系统中&#xff0c;安装软件的方式主要有四种&#xff0c;这四种安装方式的特点如下&#xff1a; 安装方式特点二进制发布包安装软件已经针对具体平台编译打包发布&#xff0c;只要解压&#xff0c;修改配置即可rpm安装软件已经按照re…

美易官方:科技巨头涨势好标普指数年底前有望升至6000点

高盛&#xff0c;作为全球领先的金融机构之一&#xff0c;近日发布了一份报告&#xff0c;预测在科技巨头的涨势推动下&#xff0c;标普500指数年底前有望升至6000点。这一预测引起了市场的广泛关注&#xff0c;投资者们纷纷开始重新评估自己的投资策略。 David Kostin等策略师…

金晟富:3.24黄金周末行情解析!周一开盘黄金分析及操作

换资共勉&#xff1a; ​ 此刻看文章的朋友你们好&#xff0c;周末愉快&#xff01;周末&#xff0c;我也习惯性的写分析&#xff0c;为的也是让大家对行情走势有所了解&#xff0c;这样在周一面对行情的时候我们也能从容应对&#xff0c;同时&#xff0c;针对手中持有多空单的…

超过 1200 个能够拦截在野外检测到的 2FA 的网络钓鱼工具包

超过 1200 个能够拦截在野外检测到的 2FA 的网络钓鱼工具包。 #################### 免责声明&#xff1a;工具本身并无好坏&#xff0c;希望大家以遵守《网络安全法》相关法律为前提来使用该工具&#xff0c;支持研究学习&#xff0c;切勿用于非法犯罪活动&#xff0c;对于恶…

202基于matlab的曲柄滑块机构的运动学仿真分析

基于matlab的曲柄滑块机构的运动学仿真分析&#xff0c;分析各个杆的速度、位移、加速度曲线&#xff0c;以及曲柄滑块机构的动画。程序已调通&#xff0c;可直接运行。 202 matlab 曲柄滑块机构 运动学仿真分析 - 小红书 (xiaohongshu.com)

第九篇【传奇开心果系列】Python自动化办公库技术点案例示例:深度解读Python处理PDF文件

传奇开心果博文系列 系列博文目录Python自动化办公库技术点案例示例系列 博文目录前言一、重要作用介绍二、Python库处理PDF文件基础操作和高级操作介绍&#xff08;一&#xff09;基础操作介绍&#xff08;二&#xff09;高级操作介绍 三、Python库处理PDF文件基础操作示例代码…

H5实现Web ECharts教程:轻松创建动态数据图表

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

【OpenBayes 官方教程】快速部署通义千问 72B 大模型

本教程主要为大家介绍怎样在 OpenBayes 上快速部署通义千文 72B 大模型&#xff0c;新朋友点击下方链接注册后&#xff0c;即可获得 4 小时 RTX 4090 5 小时 CPU 的免费使用时长哦&#xff01; 注册链接 https://openbayes.com/console/signup?ryuudi_nBBThttps://openbaye…

算法|数学与数论|素数筛

数学与数论|素数筛 1.判断素数 2.朴素筛 3.埃氏筛 4.欧拉筛(线性筛) 心有猛虎&#xff0c;细嗅蔷薇。你好朋友&#xff0c;这里是锅巴的C\C学习笔记&#xff0c;常言道&#xff0c;不积跬步无以至千里&#xff0c;希望有朝一日我们积累的滴水可以击穿顽石。 质数(素数)&…

一文读懂SPI通讯协议

一文读懂SPI通讯协议 引言 SPI(Serial Peripheral Interface)是一种同步串行通信接口协议,常用于嵌入式系统和芯片之间的通信。在本篇文章中,我们将详细介绍SPI通讯协议的工作原理、时序图和常见应用场景,并且通过丰富的示例代码帮助读者深入理解。 SPI通讯协议的基本概…

C++_第四周做题总结

id:19 A.三数论大小&#xff08;引用&#xff09; 题目描述 输入三个整数&#xff0c;然后按照从大到小的顺序输出数值。 要求&#xff1a;定义一个函数&#xff0c;无返回值&#xff0c;函数参数是三个整数参数的引用&#xff0c;例如int &a, int &b, int &c。…