【码道初阶】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 = 0
        for n in nums:
            res ^= n
        return 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. 可扩展思维:基础技巧可以衍生到更复杂的场景中

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringAI系列 - 使用LangGPT编写高质量的Prompt

目录 一、LangGPT —— 人人都可编写高质量 Prompt二、快速上手2.1 诗人 三、Role 模板3.1 Role 模板3.2 Role 模板使用步骤3.3 更多例子 四、高级用法4.1 变量4.2 命令4.3 Reminder4.4 条件语句4.5 Json or Yaml 方便程序开发 一、LangGPT —— 人人都可编写高质量 Prompt La…

为什么在springboot中使用autowired的时候它黄色警告说不建议使用字段注入

byType找到多种实现类导致报错 Autowired: 通过byType 方式进行装配, 找不到或是找到多个&#xff0c;都会抛出异常 我们在单元测试中无法进行字段注入 字段注入通常是 private 修饰的&#xff0c;Spring 容器通过反射为这些字段注入依赖。然而&#xff0c;在单元测试中&…

Ubuntu24登录PostgreSql数据库的一般方法

命令格式如 psql -U user -d db 或者 sudo psql -U user -d db 修改配置 /etc/postgresql/16/main/postgresql.conf 改成md5&#xff0c;然后重新启动pgsql sudo systemctl restart postgresql

ESP-Skainet智能语音助手,ESP32-S3物联网方案,设备高效语音交互

在科技飞速发展的今天&#xff0c;智能语音助手正逐渐渗透到我们生活的方方面面&#xff0c;而智能语音助手凭借其卓越的技术优势&#xff0c;成为了智能生活领域的一颗璀璨明星。 ESP-Skainet智能语音助手的强大之处在于其支持唤醒词引擎&#xff08;WakeNet&#xff09;、离…

数据结构与算法学习笔记----博弈论

# 数据结构与算法学习笔记----博弈论 author: 明月清了个风 first publish time: 2025.2.6 ps⭐️包含了博弈论中的两种问题Nim游戏和SG函数&#xff0c;一共四道例题&#xff0c;给出了具体公式的证明过程。 Acwing 891. Nim游戏 [原题链接](891. Nim游戏 - AcWing题库) 给…

Go 语言 | 入门 | 先导课程

快速入门 1.第一份代码 先检查自己是否有正确下载 Go&#xff0c;如果没有直接去 Go 安装 进行安装。 # 检查是否有 Go $ go version go version go1.23.4 linux/amd64然后根据 Go 的入门教程 开始进行学习。 # 初始化 Go 项目 $ mkdir example && cd example # Go…

ChatGPT提问技巧:行业热门应用提示词案例--咨询法律知识

ChatGPT除了可以协助办公&#xff0c;写作文案和生成短视频脚本外&#xff0c;和还可以做为一个法律工具&#xff0c;当用户面临一些法律知识盲点时&#xff0c;可以向ChatGPT咨询获得解答。赋予ChatGPT专家的身份&#xff0c;用户能够得到较为满意的解答。 1.咨询法律知识 举…

WPS中解除工作表密码保护(忘记密码)

1.下载vba插件 项目首页 - WPS中如何启用宏附wps.vba.exe下载说明分享:WPS中如何启用宏&#xff1a;附wps.vba.exe下载说明本文将详细介绍如何在WPS中启用宏功能&#xff0c;并提供wps.vba.exe文件的下载说明 - GitCode 并按照步骤安装 2.wps中点击搜索&#xff0c;输入开发…

【ThreeJS 01】了解 WebGL 以及 ThreeJS

文章目录 01 介绍02 什么是 WebGL&#xff0c;为什么用 ThreeJS什么是 WebGL&#xff1f;Three.js 来帮忙 01 介绍 这个课程的主讲人是 Bruno Simon&#xff0c; 这是他的作品集 他还做了一些有趣的项目&#xff1a; https://my-room-in-3d.vercel.app https://organic-sphe…

SpringBoot+Dubbo+zookeeper 急速入门案例

项目目录结构&#xff1a; 第一步&#xff1a;创建一个SpringBoot项目&#xff0c;这里选择Maven项目或者Spring Initializer都可以&#xff0c;这里创建了一个Maven项目&#xff08;SpringBoot-Dubbo&#xff09;&#xff0c;pom.xml文件如下&#xff1a; <?xml versio…

Unity Shader Graph 2D - 使用DeepSeek协助绘制一个爱心

最近十分流行使用DeepSeek AI&#xff0c;于是想尝试着能不能用DeepSeek来帮助我实现一些Shader Graph效果&#xff0c;正好之前看到了爱心图形&#xff0c;就说干脆用DeepSeek来告诉我怎么使用Shader Graph来绘制一个爱心。 问DeepSeek怎么绘制爱心 首先打开DeepSeek的网站&a…

如何正确配置您的WordPress邮件设置

在运营WordPress网站时&#xff0c;确保邮件能够顺利发送和接收是非常重要的。无论是通知、确认邮件&#xff0c;还是营销邮件&#xff0c;邮件的可靠性会直接影响用户体验。许多站长常常会遇到邮件无法送达、被标记为垃圾邮件等问题。要解决这些问题&#xff0c;使用SMTP是一个…

MySQL调优01 - 单库调优思想

单库调优 文章目录 单库调优一&#xff1a;系统中性能优化的核心思维二&#xff1a;MySQL性能优化实践1&#xff1a;连接层的优化1.1&#xff1a;连接数是越大越好吗&#xff1f;1.2&#xff1a;偶发高峰类业务的连接数配置1.3&#xff1a;分库分表情况下的连接数配置1.4&#…

OLED显示屏使用学习——(二)

四、OLED 原理图设计注意事项 4.1 SPI 接口设计 在 SPI 接口中需保证 BS0,BS1,BS2 全为 0&#xff0c;也不是接地&#xff1b;所以在接口配置电阻中 4.2 IIC 接口设计 在 iic 接口中需要将 BS1 配置为 1&#xff0c;BS0 为 0&#xff1b;所以 R1,R4 焊接&#xff0c;R2&am…

string类OJ练习题

目录 文章目录 前言 一、反转字符串 二、反转字符串 II 三、反转字符串中的单词 III 四、验证一个字符串是否是回文 五、字符串相加&#xff08;大数加法&#xff09; 六、字符串相乘&#xff08;大数乘法&#xff09; 七、把字符串转化为整数&#xff08;atoi&#xff09; 总结…

6-图像金字塔与轮廓检测

文章目录 6.图像金字塔与轮廓检测(1)图像金字塔定义(2)金字塔制作方法(3)轮廓检测方法(4)轮廓特征与近似(5)模板匹配方法6.图像金字塔与轮廓检测 (1)图像金字塔定义 高斯金字塔拉普拉斯金字塔 高斯金字塔:向下采样方法(缩小) 高斯金字塔:向上采样方法(放大)…

javaEE-6.网络原理-http

目录 什么是http? http的工作原理&#xff1a; 抓包工具 fiddler的使用 HTTP请求数据: 1.首行:​编辑 2.请求头(header) 3.空行&#xff1a; 4.正文&#xff08;body&#xff09; HTTP响应数据 1.首行&#xff1a;​编辑 2.响应头 3.空行&#xff1a; 4.响应正文…

网络安全-防御 第一次作业(由于防火墙只成功启动了一次未补截图)

防火墙安全策略课堂实验报告 一、拓扑 本实验拓扑包含预启动设备、DMZ区域&#xff08;含OA Server和Web Server&#xff09;、防火墙&#xff08;FW1&#xff09;、Trust区域&#xff08;含办公区PC和生产区PC&#xff09;等。具体IP地址及连接关系如给定拓扑图所示&#xf…

开源项目介绍-词云生成

开源词云项目是一个利用开源技术生成和展示词云的工具或框架&#xff0c;广泛应用于文本分析、数据可视化等领域。以下是几个与开源词云相关的项目及其特点&#xff1a; Stylecloud Stylecloud 是一个由 Maximilianinir 创建和维护的开源项目&#xff0c;旨在通过扩展 wordclou…

使用DeepSeek的技巧笔记

来源&#xff1a;新年逼自己一把&#xff0c;学会使用DeepSeek R1_哔哩哔哩_bilibili 前言 对于DeepSeek而言&#xff0c;我们不再需要那么多的提示词技巧&#xff0c;但还是要有两个注意点&#xff1a;你需要理解大语言模型的工作原理与局限,这能帮助你更好的知道AI可完成任务…