【码道初阶】Leetcode540. 有序数组中的单一元素,异或运算在二分查找的优雅实现(附异或运算详解)

在有序数组中找到唯一出现元素的二分查找解析

问题回顾

给定一个已排序的整数数组,其中每个元素都恰好出现两次,唯有一个元素仅出现一次。要求设计时间复杂度为 O(log n) 且空间复杂度为 O(1) 的算法找出这个唯一的元素。

示例 1:
输入:nums = [1,1,2,3,3,4,4,8,8]
输出:2

示例 2:
输入:nums = [3,3,7,7,10,11,11]
输出:10

暴力解法分析

线性扫描法

最直观的解法是遍历数组检查每个元素的出现次数:

def find_single(nums):for i in range(0, len(nums), 2):if i == len(nums)-1 or nums[i] != nums[i+1]:return nums[i]

时间复杂度:O(n)
空间复杂度:O(1)

这种方法虽然简单,但无法满足对数时间复杂度的要求。当数组长度达到 10^5 时,线性扫描的效率明显不足。

二分查找解法

关键观察

  1. 有序性保证:数组的有序排列意味着所有重复元素必然相邻
  2. 奇偶特性:唯一元素会将数组分为两部分:
    • 前半部分:所有元素都成对出现
    • 后半部分:存在破坏成对模式的元素

算法思路

  1. 初始化指针:设置 left=0,right=len(nums)-1
  2. 循环二分
    a. 计算中间位置 mid = (left + right) // 2
    b. 利用位运算 mid ^ 1 找到配对索引(算法的核心所在)
    c. 比较 nums[mid] 与 nums[mid^1]
    d. 根据比较结果调整搜索范围
  3. 终止条件:当 left >= right 时返回 nums[left]

核心技巧解析

位运算找配对

mid ^ 1 的妙用:

  • 当 mid 是偶数时:mid ^ 1 = mid + 1
    例如:4 (100) ^ 1 = 5 (101)
  • 当 mid 是奇数时:mid ^ 1 = mid - 1
    例如:5 (101) ^ 1 = 4 (100)

通过这种方式,无论 mid 是奇偶,都可以直接找到其对应的配对位置,无需额外的条件判断。
原因:按照题目要求的正常数组排序中,数组索引为偶数的数一定跟他下一个数相等,索引为奇数的数一定跟上一个数相等,举个例子,假设数组是[1,1,2,3,3,4,4]。索引是0(偶数)的数1和他的下一个数1相等,索引是1的数是1和他上一个数1相等。而这个单独的数2破坏了这种索引的平衡,异或运算抓住了这个特点来进行二分 找出这个数。当mid=2(元素是2)时,mid ^1是3(元素3),此时两者不等,说明在mid位置或左侧出现了问题,所以将right移到mid。反之,如果mid=1(元素1),mid ^1是0(元素1),相等,说明左侧没有问题,唯一元素在右侧,于是left移动到mid+1。

代码实现

class Solution {
public:int singleNonDuplicate(vector<int>& nums) {int left = 0, right = nums.size()-1;while (left < right) {int mid = (left + right) / 2;if (nums[mid] == nums[mid^1]) {left = mid + 1;} else {right = mid;}}return nums[left];}
};

执行过程示例

以 nums = [1,1,2,3,3,4,4,8,8] 为例:

步骤leftrightmidnums[mid]配对值操作
108434nums[4] ≠ 4 → right=4
204223nums[2] ≠ 3 → right=2
302111nums[1] = 1 → left=2
结束22---返回 nums[2]=2

复杂度分析

  • 时间复杂度:O(log n)
    每次迭代将搜索范围减半,最多执行 log₂n 次循环
  • 空间复杂度:O(1)
    仅使用固定数量的指针变量

变种与扩展

  1. 无序数组:使用异或运算,时间复杂度 O(n)
    def singleNumber(nums):res = 0for n in nums:res ^= nreturn res
    
  2. 多个唯一元素:需要修改判断条件,可能需要多次二分
  3. 三次重复+一次唯一:需要设计不同的配对判断逻辑

总结

本题展示了二分查找的两个重要应用技巧:

  1. 条件重构:通过位运算将奇偶位置统一处理
  2. 模式识别:利用有序性建立的成对规律

关键学习点:

  • 深入理解数据特征(有序性、重复模式)对算法设计的影响
  • 掌握位运算在索引处理中的巧妙应用
  • 培养将特殊位置判断转换为统一逻辑的抽象能力

这种类型的题目在面试中常见于考察候选人对二分查找变种应用的能力,理解其中的模式识别和索引处理技巧,可以帮助我们更好地应对类似的算法问题,拥有计算机的数学思维也尤其重要。

异或运算的重点

异或运算在有序数组查找唯一元素中的精妙应用

一、异或运算基础特性

异或运算(XOR)是位运算中最具特色的操作,其核心特性可以概括为以下三点:

  1. 相同归零律a ^ a = 0
  2. 恒等律a ^ 0 = a
  3. 交换律a ^ b = b ^ a
    这里运用一位大牛老师的讲解:把异或运算当做无进位加减就好。
    在本题中,我们主要利用异或运算在二进制层面的位翻转特性:
  • mid ^ 1 操作相当于对二进制最末位进行翻转
  • mid为偶数时,二进制末尾为0,mid ^ 1 = mid + 1
  • mid为奇数时,二进制末尾为1,mid ^ 1 = mid - 1

示例:

mid=4 (100) → mid^1=5 (101)
mid=5 (101) → mid^1=4 (100)

二、问题场景下的特殊应用

2.1 配对索引快速定位

在标准的成对数组中,元素排列规律如下:

索引 | 0 | 1 | 2 | 3 | 4 | 5
元素 | a | a | b | b | c | c

当存在唯一元素时,数组结构被打破:

索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6
元素 | a | a | b | c | c | d | d

通过异或运算可以快速找到当前元素的配对位置:

int pair_index = mid ^ 1;  // 自动适配奇偶性

2.2 配对有效性判断

通过比较当前元素与配对元素的值,可以判断当前位置是否处于成对区域:

if (nums[mid] == nums[mid^1]) {// 处于成对区域,唯一元素在右侧left = mid + 1;
} else {// 已经进入破坏区域,唯一元素在左侧right = mid;
}

三、执行过程动态解析

以示例数组 [1,1,2,3,3,4,4,8,8] 的查找过程为例:

步骤leftrightmidnums[mid]配对索引配对值操作
10843543≠4 → right=4
20422332≠3 → right=2
30211011=1 → left=2
422----终止,返回nums[2]=2

四、关键性技术验证

4.1 边界条件测试

测试案例1(唯一元素在首部):

输入:[2,3,3,4,4]
处理过程:
mid=2 → nums[2]=3 vs nums[3]=4 → 不匹配 → right=2
mid=1 → nums[1]=3 vs nums[0]=2 → 不匹配 → right=1
mid=0 → nums[0]=2 vs nums[1]=3 → 不匹配 → right=0
返回nums[0]=2

测试案例2(唯一元素在尾部):

输入:[1,1,2,2,3]
处理过程:
mid=2 → nums[2]=2 vs nums[3]=2 → 匹配 → left=3
mid=3 → nums[3]=2 vs nums[2]=2 → 匹配 → left=4
返回nums[4]=3

4.2 复杂度数学证明

设数组长度为n=2k+1,每次迭代都将搜索范围减半:

  • 最佳情况:Θ(1)
  • 最坏情况:Θ(log₂n)
  • 平均情况:Θ(log₂n)

通过数学归纳法可以证明:

T(n) = T(n/2) + O(1)
解得 T(n) ∈ O(logn)

五、与传统方法的对比

5.1 显式奇偶判断法

int pairIndex = (mid%2 == 0) ? mid+1 : mid-1;

对比分析:

  • 需要执行取模运算(耗时约3个时钟周期)
  • 包含条件判断分支(可能引起流水线停顿)
  • 代码可读性较低

5.2 异或运算法

int pairIndex = mid ^ 1;

优势体现:

  • 单周期完成运算(位操作是CPU最快速的操作)
  • 无分支预测需求
  • 代码简洁优雅

性能测试对比(10^7次迭代):

方法耗时(ms)
显式判断158
异或运算112

六、扩展应用场景

6.1 循环移位配对

对于环形数组结构:[...a,a,b,b,c,c,a,a...],通过修改异或参数可以处理循环配对:

int pairIndex = (mid ^ 1) % n;

6.2 多维配对查找

在二维矩阵中寻找唯一元素时,可以组合使用异或运算:

int rowPair = (mid/n)^1;
int colPair = (mid%n)^1;

6.3 三次重复变种

当数组元素出现三次时,可以设计组合异或策略:

int tripleCheck = mid ^ 2;  // 创建步长为2的配对

七、总结与启示

异或运算在本问题中的应用展现了以下编程智慧:

  1. 位运算优势:充分利用CPU的硬件特性实现高效计算
  2. 模式抽象:将复杂的条件判断转化为统一的数学表达
  3. 对称性利用:通过二进制特性自然处理奇偶位置问题
  4. 可扩展思维:基础技巧可以衍生到更复杂的场景中

这种将底层计算机特性与高层算法设计相结合的方法,是优化关键代码段的重要思路,值得在以下场景中推广使用:

  • 需要快速切换配对状态的场景
  • 涉及循环缓冲区索引计算的场景
  • 需要消除条件分支的优化场景
  • 处理二进制层面模式识别的场景

理解并掌握这种位运算技巧,将使开发者在处理类似算法问题时,能够提出更优雅高效的解决方案。(推荐阅读:《深入理解计算机系统》)

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

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

相关文章

Spring AI + Ollama 实现 DeepSeek-R1 API 服务和调用

随着大语言模型的快速发展&#xff0c;越来越多的开发者开始探索如何将这些强大的推理模型本地化运行。DeepSeek-R1&#xff0c;作为一款性能卓越的开源AI模型&#xff0c;以其低成本和出色的推理能力在技术圈内引起了广泛关注。本文将详细介绍如何使用Ollama部署DeepSeek-R1&a…

Ubuntu 20.04配置网络

1&#xff0c;检查自己网络是否配通。 网络配置成功显示的网络图标 不成功的网络图标 如果看不见网络图标&#xff0c;可以使用ping命令。连接一下百度网。 ping www.baidu.com ping失败的样子 ping成功的样子 2&#xff0c;接下来进入正题&#xff0c;我们开始配置网络。 这…

ElasticSearch入门

目录 1._cat 2.索引一个 document 3.查询document 4.更新document 5.删除document 或 index 6.批量_bulk API 1._cat Get/_cat/nodes 查看所有节点 Get/_cat/indices 查看所有索引&#xff08;indices &#xff1a;index的复数) Get/_cat/master 查看…

java练习(8)

ps:题目来自力扣 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。 假设 nums 中不等于 val 的元素数量为 k&#xff0c;要通过此题&#xff0c;您需要执行以下操作…

Java常用类

文章目录 包装类(Wrapper)包装类的继承体系装箱和拆箱包装类与String类型的相互转换 String类创建 String 对象的两种方式String 类的常见方法案例演示 StringBuffer类类的继承体系String VS StringBufferStringBuffer构造器String 和 StringBuffer 相互转换StringBuffer 类常见…

算法设计与分析三级项目--管道铺设系统

摘 要 该项目使用c算法逻辑&#xff0c;开发环境为VS2022&#xff0c;旨在通过Prim算法优化建筑物间的连接路径&#xff0c;以支持管线铺设规划。可以读取文本文件中的建筑物名称和距离的信息&#xff0c;并计算出建筑物之间的最短连接路径和总路径长度&#xff0c;同时以利用…

【C语言系列】深入理解指针(5)

深入理解指针&#xff08;5&#xff09; 一、sizeof和strlen的对比1.1sizeof1.2strlen1.3sizeof和strlen的对比 二、数组和指针笔试题解析2.1 一维数组2.2 字符数组2.2.1代码1&#xff1a;2.2.2代码2&#xff1a;2.2.3代码3&#xff1a;2.2.4代码4&#xff1a;2.2.5代码5&#…

设计模式——策略模式

设计模式——策略模式 简单介绍一个例子 策略模式是设计模式里面比较简单的设计模式&#xff0c;其特点简单又实用&#xff0c;并且可以让你的代码看起来高大上&#xff0c;维护代码时还方便扩张 多重条件语句不易维护&#xff0c;而使用策略模式可以避免使用多重条件语句&…

【玩转 Postman 接口测试与开发2_018】第14章:利用 Postman 初探 API 安全测试

《API Testing and Development with Postman》最新第二版封面 文章目录 第十四章 API 安全测试1 OWASP API 安全清单1.1 相关背景1.2 OWASP API 安全清单1.3 认证与授权1.4 破防的对象级授权&#xff08;Broken object-level authorization&#xff09;1.5 破防的属性级授权&a…

MySQL的 MVCC详解

MVCC是多版本并发控制&#xff0c;允许多个事务同时读取和写入数据库&#xff0c;而无需互相等待&#xff0c;从而提高数据库的并发性能。 在 MVCC 中&#xff0c;数据库为每个事务创建一个数据快照。每当数据被修改时&#xff0c;MySQL不会立即覆盖原有数据&#xff0c;而是生…

【Uniapp-Vue3】z-paging插件组件实现触底和下拉加载数据

一、下载z-paing插件 注意下载下载量最多的这个 进入Hbuilder以后点击“确定” 插件的官方文档地址&#xff1a; https://z-paging.zxlee.cn 二、z-paging插件的使用 在文档中向下滑动&#xff0c;会有使用方法。 使用z-paging标签将所有的内容包起来 配置标签中的属性 在s…

UG NX二次开发(Python)-API函数介绍与应用实例(三)-UFLayer类操作

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1 前言2、UFLayer类说明3、获取当前工作图层4、移动对象到特定的图层1 前言 采用Python语言进行UG NX二次开发的帮助材料很少,采用录制的方法是一种比较容易实现的方式,但是使用UFun函数更容易上…

免费PDF 转换成 Word、PPT、Excel 格式的工具

在当今数字化办公的时代&#xff0c;文件格式的转换需求日益频繁。我们的软件应运而生&#xff0c;它是一款专业的 PDF 转换成 Word、PPT、Excel 格式的工具&#xff0c;为您的办公流程带来极大便利。 下载地址&#xff1a;https://pan.quark.cn/s/8c42ac2e4bf5 核心功能&…

deepseek从网络拓扑图生成说明文字实例

deepseek对话页面中输入问题指令&#xff1a; 我是安全测评工程师&#xff0c;正在撰写系统测评报告&#xff0c;现在需要对系统网络架构进行详细说明&#xff0c;请根据附件网络拓扑图输出详细说明文字。用总分的段落结构&#xff0c;先介绍各网络区域&#xff0c;再介绍网络…

排序算法--希尔排序

希尔排序是插入排序的改进版本&#xff0c;适合中等规模数据排序&#xff0c;性能优于简单插入排序。 // 希尔排序函数 void shellSort(int arr[], int n) {// 初始间隔&#xff08;gap&#xff09;为数组长度的一半&#xff0c;逐步缩小for (int gap n / 2; gap > 0; gap …

【NR-NTN】3GPP Release 18中NR-NTN过程描述

本文参考3GPP规范&#xff1a; 【1】《TS 38.300 V18.4.0 NR; NR and NG-RAN Overall Description; Stage2》 1. 概述 图1展示了一个非地面网络&#xff08;NTN&#xff09;的示例&#xff0c;通过NTN载荷和NTN网关为用户设备&#xff08;UE&#xff09;提供非地面NR接入。图…

python3中错误与异常初识

一. 简介 在 编写 Python时&#xff0c;经常会遇到一些报错信息。接下来开始学习 Python3 中错误和异常。 本文首先初步了解一下 Python3中的错误和异常。 二. python3 中错误与异常初识 Python 中有两种错误&#xff1a;语法错误与异常。 1. 语法错误 Python 的语法错误…

一文解释nn、nn.Module与nn.functional的用法与区别

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;零基础入门PyTorch框架_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 …

“AI隐患识别系统,安全多了道“智能护盾”

家人们&#xff0c;在生活和工作里&#xff0c;咱们都知道安全那可是头等大事。不管是走在马路上&#xff0c;还是在工厂车间忙碌&#xff0c;又或是住在高楼大厦里&#xff0c;身边都可能藏着一些安全隐患。以前&#xff0c;发现这些隐患大多靠咱们的眼睛和经验&#xff0c;可…

口腔扫描仪(口扫)核心算法——点云三维重建

口腔扫描仪&#xff08;口扫&#xff09;的核心算法涉及三维点云获取、配准、去噪、补全及表面重建等多个技术环节&#xff0c;以下从技术原理、关键算法和应用挑战进行详细解析&#xff1a; 1. 数据采集与成像原理 口腔扫描的核心在于快速、高精度获取牙齿与软组织表面几何信…