【C++指南】“单身狗问题”——只出现一次的数字 系列问题

article/2025/8/15 20:53:52

.

💓 博客主页:倔强的石头的CSDN主页
📝Gitee主页:倔强的石头的gitee主页
⏩ 文章专栏:《C++指南》
期待您的关注
在这里插入图片描述

文章目录

  • 引言
    • 一、只出现一次的数字(一)简单
      • 题目描述
      • 解题思路
      • 代码实现及解释
    • 二、只出现一次的数字 (二)中等
      • 题目描述
      • 解题思路
      • 代码实现及解释
    • 三、只出现一次的数字 (三)困难
      • 题目描述
      • 解题思路
      • 代码实现及解释
    • 解题总结
      • 共性
      • 差异

引言

在算法领域,“只出现一次的数字”系列题目是经典的位运算应用题型,这类问题又被形象的称为“单身狗问题”。
这一系列题目通过不同的数字出现次数设定,考查我们对位运算特性的理解和运用能力
下面我们将对三道相关题目进行深入剖析。

位运算知识可阅读配套文章:
【C++指南】位运算知识详解

一、只出现一次的数字(一)简单

题目描述

给定一个非空整数数组 nums,除了某个元素只出现一次以外,其余每个元素均出现两次。要求找出那个只出现了一次的元素,并且必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。

解题思路

本题的关键在于利用位运算中的异或运算(^)特性。异或运算具有以下性质:

  • 一个数与自身异或结果为 0,例如 a ^ a = 0
  • 任何数与 0 异或结果为其本身,例如 a ^ 0 = a
  • 异或运算满足交换律和结合律,即 a ^ b = b ^ a(a ^ b) ^ c = a ^ (b ^ c)

基于这些性质,我们可以将数组中所有数字依次进行异或操作。由于出现两次的数字会相互抵消(异或为 0 ),最终得到的结果就是只出现一次的那个数字。

代码实现及解释

class Solution {
public:int singleNumber(vector<int>& nums) {int ret = 0;// 遍历数组中的每一个元素for (auto i : nums) {// 将ret与当前元素i进行异或操作ret ^= i; }return ret;}
};
  • int ret = 0; :初始化一个变量 ret 并赋值为 0,用于存储最终的异或结果。
  • for (auto i : nums) :这是 C++ 11 引入的范围 for 循环,用于遍历数组 nums 中的每一个元素,将当前元素依次赋值给 i
  • ret ^= i; :等价于 ret = ret ^ i,将 ret 与当前元素 i 进行异或操作。在遍历过程中,出现两次的元素会相互抵消(异或为 0 ),最终 ret 就会是只出现一次的那个数字。

二、只出现一次的数字 (二)中等

题目描述

给定一个整数数组 nums,除某个元素仅出现一次外,其余每个元素都恰出现三次。需要找出并返回那个只出现了一次的元素,并且必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题。

解题思路

对于本题,我们采用位运算的思路,考虑整数的 32 个二进制位。因为除目标数字外其他数字都出现三次,所以我们可以对数组中所有数的每一位二进制位进行统计·

具体来说,对于每一位二进制位,统计数组中所有元素在该位上 1 出现的次数。
由于其他数字都出现三次,那么该位上 1 出现的次数对 3 取余的结果,就是目标数字在该位上的值。(如果出现3次,取模为0;只出现一次,取模为1)
通过对 32 位二进制位都进行这样的操作,我们就能还原出只出现一次的那个数字。

代码实现及解释

class Solution {
public:int singleNumber(vector<int>& nums) {int res = 0;// 循环遍历每一位二进制位,从第0位到第31位for (int i = 0; i < 32; ++i) { int sum = 0;// 遍历数组中的每一个元素for (auto num : nums) { // 右移i位,再与1逻辑与,统计当前位上1出现的次数sum += ((num >> i) & 1); }// 如果这一位上1出现的次数对3取余为1,将它加到结果res对应的二进制位上res += (sum % 3) << i; }return res;}
};
  • int res = 0; :初始化结果变量 res ,用于存储最终找到的只出现一次的数字。
  • for (int i = 0; i < 32; ++i) :外层循环用于遍历整数的 32 个二进制位,从第 0 位到第 31 位。
  • int sum = 0; :在每次遍历新的二进制位时,初始化 sum0,用于统计当前位上 1 出现的次数。
  • for (auto num : nums) :内层循环遍历数组 nums 中的每一个元素 num
  • sum += ((num >> i) & 1); :将 num 右移 i 位,使得当前要统计的二进制位移到最低位,然后与 1 进行逻辑与操作。如果该位为 1,结果为 1;如果该位为 0,结果为 0。将这个结果累加到 sum 中,实现对当前位上 1 出现次数的统计。
  • res += (sum % 3) << i; :对 sum3 取余,如果结果为 1,说明目标数字在当前二进制位上是 1,将 1 左移 i 位后加到 res 中,实现对结果数字对应二进制位的设置。

三、只出现一次的数字 (三)困难

题目描述

给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。需要找出只出现一次的那两个元素,可以按任意顺序返回答案,并且必须设计并实现线性时间复杂度的算法且仅使用常量额外空间来解决此问题。

解题思路

本题的解题思路基于异或运算和分组的思想。

  1. 首先,将数组中所有元素进行异或操作。根据异或运算的性质,出现两次的元素会相互抵消(异或为 0 ),最终得到的结果是两个只出现一次元素的异或值
  2. 然后,找到这个异或值中从右往左第一个为 1 的位。因为这两个只出现一次的元素在该位上的值不同(异或结果为 1 说明两个操作数该位不同),所以可以根据这一位是 1 还是 0 将原数组分成两组。
  3. 最后,分别对这两组元素进行异或操作。由于分组后每组中除了目标元素外其他元素都出现两次,所以每组异或的结果就是对应的那个只出现一次的元素

代码实现及解释

vector<int> singleNumber(vector<int>& nums) {int ret = 0;// 遍历数组,将所有元素异或,得到两个只出现一次元素的异或结果for (auto i : nums) { ret ^= i; }int rightone = 0;// 从右往左找到异或结果中第一个为1的位for (int i = 0; i < 32; ++i) { if (((ret >> i) & 1) == 1) {rightone = 1 << i;break;}}int num1 = 0;int num2 = 0;// 根据找到的位将数组元素分组并分别异或for (auto i : nums) { if (i & rightone) {num1 ^= i;} else {num2 ^= i;}}return {num1, num2}; 
}
  • int ret = 0; :初始化变量 ret0,用于存储数组所有元素异或的结果。
  • for (auto i : nums) :遍历数组 nums 中的每一个元素 i,将 reti 进行异或操作,最终 ret 是两个只出现一次元素的异或结果。
  • int rightone = 0; :初始化变量 rightone0,用于存储从右往左找到的异或结果中第一个为 1 的位。
  • for (int i = 0; i < 32; ++i) :循环遍历 32 个二进制位,寻找第一个为 1 的位。
  • if (((ret >> i) & 1) == 1) :将 ret 右移 i 位,判断当前位是否为 1 。如果是 1 ,则执行以下操作。
  • rightone = 1 << i; :将 1 左移 i 位,得到表示该位为 1 的掩码。
  • break; :找到第一个为 1 的位后,跳出循环。
  • int num1 = 0; int num2 = 0; :初始化两个变量 num1num2 ,用于分别存储两组异或的结果。
  • for (auto i : nums) :再次遍历数组 nums 中的每一个元素 i
  • if (i & rightone) :判断元素 i 在找到的位上是否为 1 。如果是 1 ,则将 inum1 进行异或操作;否则,将 inum2 进行异或操作。
  • return {num1, num2}; :返回包含两个只出现一次元素的数组。

解题总结

“只出现一次的数字”系列题目围绕着在不同数字出现次数规则下找出特殊数字展开,主要通过位运算来解决。

共性

  • 位运算的运用:充分利用异或运算的特性,异或运算在处理出现次数有规律(如出现两次、三次)的场景中起到关键作用。对于出现两次的数字,异或可使其相互抵消;对于更复杂的出现次数情况,通过对二进制位的统计和分组来处理。
  • 空间和时间复杂度要求:都要求线性时间复杂度(通常是一次遍历数组)和常量级额外空间,这就限制了不能使用额外的数据结构来存储中间结果,必须在位运算的思路上进行巧妙设计。

差异

  • 题目 一:数字出现情况相对简单,除一个数字外其他都出现两次,直接利用异或运算依次对数组元素操作即可得到结果。
  • 题目 二:由于多数数字出现三次,不能简单用异或抵消,而是通过对每一位二进制位上 1 出现次数统计并对 3 取余来确定目标数字每一位的值。
  • 题目 三:存在两个只出现一次的数字,先通过整体异或得到两个目标数字的异或结果,再找到异或结果中特定的位进行分组,分别异或得到两个目标数字。

解决这类题目时,关键是深入理解位运算的特性,根据数字出现次数的特点,合理设计对二进制位的操作方式,通过统计、分组等手段实现高效的算法。

本文完

关于位运算知识可阅读配套文章:
【C++指南】位运算知识详解


http://www.hkcw.cn/article/iBbpAVMFfH.shtml

相关文章

优化 InfluxDB 写入性能:高效批处理策略实战指南

在处理高吞吐量时序数据时&#xff0c;合理运用批处理&#xff08;Batching&#xff09;策略是提升 InfluxDB 写入性能的关键。本文介绍 时间驱动、大小驱动和混合批处理策略&#xff0c;并通过 Python 代码示例展示如何优化数据写入&#xff0c;平衡 延迟与吞吐量。同时&#…

RedwoodJS:乱拳打倒老师傅 NextJS!

RedwoodJS 是一个全栈的 JavaScript/TypeScript 框架&#xff0c;其作用是帮助开发者高效地构建现代化的 Web 应用。它将前端、后端和数据库集成在一起&#xff0c;并使用一种“JAMstack”架构&#xff08;JavaScript、API 和 Markup&#xff09;来构建可扩展的应用程序。 Star…

【C++】 —— 笔试刷题day_18

一、压缩字符串(一) 题目解析 题目给定一个字符str&#xff0c;让我们将这个字符串进行压缩&#xff1b; **压缩规则&#xff1a;**出现多次的字符压缩成字符数字&#xff1b;例如aaa压缩成a3。如果字符值出现一次&#xff0c;1不用写。 算法思路 这道题总的来说就非常简单了…

谷歌浏览器如何禁用javaScript

通过禁用js&#xff0c;可以访问一些设置权限的内容。 Chrome 地址栏输入 chrome://settings/content 回车。 找到 JavaScript 选项。 切换为 不允许网站使用 JavaScript。 地址栏输入&#xff1a; chrome://settings/content/javascript?searchJavaScript Firefox 地址栏输入…

Java从入门到“放弃”(精通)之旅——类和对象全面解析⑦

Java从入门到“放弃”&#xff08;精通&#xff09;之旅&#x1f680;——类和对象全面解析⑦ 一、面向对象初探 1.1 什么是面向对象&#xff1f; Java是一门纯面向对象的语言(OOP)&#xff0c;在面向对象的世界里&#xff0c;一切皆为对象。面向对象是解决问题的一种思想&am…

【Golang】第七弹----map

笔上得来终觉浅,绝知此事要躬行 &#x1f525; 个人主页&#xff1a;星云爱编程 &#x1f525; 所属专栏&#xff1a;Golang &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 1基本介绍 Go语言中的 map …

C/C++程序员为什么要了解汇编?了解汇编有哪些好处?如何学习汇编?

目录 1、概述 2、从汇编的角度去理解问题的若干实例说明 2.1、使用空指针去访问类的数据成员或调用类的虚函数为什么会引发崩溃? 2.2、从汇编代码的角度去理解多线程的执行细节,去理解多线程在访问共享资源时为什么要加锁 2.3、使用Windbg静态分析dump时先从崩溃的那条汇…

基于谐波线性化方法的跟网型GFL并网变流器/VSC宽频序阻抗建模及扫频(Matlab/Simulink平台)及文献复现

目录 1、课程及模型介绍 2、谐波线性化方法介绍 3、跟网型及构网型并网变流器的特点 4、跟网型变流器/VSC拓扑及控制结构 5、不同坐标系下VSC序阻抗建模推导过程 5.1 abc三相坐标系下的VSC序阻抗建模 5.2 d-q旋转坐标系下的VSC序阻抗建模 5.2.1 Park变换及频率偏移效应…

C++“STL之String”

​ 🌹个人主页🌹:喜欢草莓熊的bear 🌹专栏🌹:C++入门 目录 ​编辑 前言 一、STL简介 1.1 STL是什么? 1.2 STL的版本(这个不是很重要了解即可) 1.3 STL的六大组件 二、 String类 2.1为什么要学习String类? 2.1.1 C语言中的字符串…

C++之多态

开始新的征程啦———多态&#xff0c;它也是C的三大特性之一。 文章目录 一、多态的概念二、多态的定义和实现2.1多态的定义2.2 实现动态多态所需要的条件&#xff08;2个&#xff09;2.3 虚函数的定义2.4 虚函数的重写/覆盖2.5 虚函数重写中的问题2.5.1 协变2.5.2 析构函数的…

【第十六届蓝桥杯省赛】比赛心得与经验分享(PythonA 组)

文章目录 一、我的成绩二、我的备赛经历三、如何备赛&#xff08;个人观点&#xff09;1. 基础语法2. 数据结构3. 算法4. 数学 四、做题技巧与注意事项五、我的题解试题A 偏蓝 &#x1f3c6;100%试题B IPV6 &#x1f3c6;0%试题C 2025图形 &#x1f3c6;100%试题D 最大数字 &am…

21天Python计划:零障碍学语法(更新完毕)

文章目录 前言Python 部分MySQL 部分目录结语资料截图 前言 此技术博客专栏围绕 Python 编程和 MySQL 数据库展开了系统且循序渐进的知识讲解&#xff0c;共包含 21 篇文章。 Python 部分 从基础入门逐步深入到高级应用。首先介绍了 Python 的下载和开发工具&#xff0c;为后续…

JavaScript--js基础(详细 全面)

目录 前言: JavaScript 是什么&#xff1f;JavaScript 简介 1.JavaScript历史 2.JavaScript 具有以下特点 第一个JavaScript程序 1.在脚本文件中编写JavaScript代码 2.JavaScript代码执行顺序 基本语法 1.变量 2.数据类型 3.算术运算符 4.赋值运算 5.字符串运算符 6…

Java识别图片或扫描PDF中的文字

目录 使用工具 Java识别图片中的文字 Java识别扫描PDF中的文字 注意事项 图片和扫描文件通常以非文本格式存在&#xff0c;这使得其中的文字信息难以直接编辑、搜索或复制。为了解决这个问题&#xff0c;光学字符识别&#xff08;OCR&#xff09;技术应运而生。OCR通过分析…

【C++】C++11新特性详解:可变参数模板与emplace系列的应用

C语法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;命名空间缺省参数与函数重载C相关特性类和对象-上篇类和对象-中篇类和对象-下篇日期类C/C内存管理模板初阶String使用String模拟实现Vector使用及其模拟实现List使用及其模拟实现容器适配器Stack与QueuePriori…

使用宝塔面板快速部署SpringBoot+Vue项目(Java + Node)

使用宝塔面板快速部署SpringBootVue项目&#xff08;Java Node&#xff09; 项目主要技术栈准备工作1. 一台云服务器&#xff08;阿里云、腾讯云等&#xff09;&#xff0c;我这里使用的是阿里云的服务器&#xff08;2核2G&#xff09;2. 已安装宝塔面板3. 已开发完成的Spring…

一文弄懂 | YOLOv8网络结构解读 、yolov8.yaml配置文件详细解读与说明、模型训练参数详细解析 | 通俗易懂!入门必看系列!

看这一篇就够了。本文内含YOLOv8网络结构图 yaml配置文件详细解读与说明 训练教程 训练参数设置参数解析说明等一些有关YOLOv8的内容&#xff01; YOLOv8v10专栏订阅链接&#xff1a;YOLOv10 创新改进高效涨点持续改进300多篇永久免费答疑 &#xff08;订阅的小伙伴&#xf…

[C++][第三方库][ODB]详细讲解

目录 1.介绍2.安装1.安装 build22.安装 odb-compiler3.安装 ODB 运行时库4.安装MySQL和客户端开发包5.安装 boost profile 库6.总体操作7.测试样例 3.ODB 常见操作1.ODB 类型映射2.ODB 编程1.指令2.示例 4.类与接口5.使用 1.介绍 ODB框架&#xff1a;数据库ORM框架 --> 对象…

【Python】解决Python报错:ERROR: Could not find a version that satisfies the requirement

成功解决Python报错&#xff1a;ERROR: Could not find a version that satisfies the requirement。ERROR: Could not find a version that satisfies the requirement 是 Python 的包管理工具 pip 在安装包时可能遇到的错误。这通常意味着 pip 没有找到与给定版本要求匹配的包…

C语言 —— 此去经年梦浪荡魂音 - 深入理解指针(卷五)

目录 1. sizeof 和 strlen的区别 1.1 sizeof 1.2 strlen 2. 数组和指针习题解析 2.1 一维数组 2.2 字符数组 代码1&#xff1a; 代码2&#xff1a; 代码3: 代码4&#xff1a; 代码5&#xff1a; 代码6&#xff1a; 2.3 二维数组 3. 指针运算笔试题解析 3.1 3.…