算法题:只出现一次的数字

题目:

给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。

示例 1 :

**输入:**nums = [2,2,1]

**输出:**1

示例 2 :

**输入:**nums = [4,1,2,1,2]

**输出:**4

示例 3 :

**输入:**nums = [1]

**输出:**1

看到题目时,第一反应是使用hashmap存储每个元素及元素出现的次数,然后将出现次数为1的座位返回值给出。但这种靠直觉做出来的方法提交上去往往排在低效的20%中。

异或算法

后来看到前90%的高效算法 都是使用异或算法做的,实现方式如下:

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

    static void main() {
        int[] nums = {4,4,1,2,1,2,3};
        System.out.println(test1(nums));
    }

    static int test1(int[] nums) {
        int n = 0;
        for (int num : nums) {
            n = n ^ num;
        }
        return n;
    }
}

异或运算的三个"超能力"

在二进制的世界里,异或运算的规则是:相同为0,不同为1。基于这个规则,它衍生出了三个极其重要的性质:

  1. 归零律(自己打自己) :任何数和自己做异或,结果都是 0。
    • 比如:a ^ a = 0
  2. 恒等律(和平主义者) :任何数和 0 做异或,结果还是它自己。
    • 比如:a ^ 0 = a
  3. 交换律和结合律(随意插队) :异或运算的顺序可以随便换,不影响最终结果。
    • 比如:a ^ b ^ a 等价于 a ^ a ^ b

为什么这段代码能找出"单身狗"?

让我们把这三个性质结合起来看。题目告诉我们:"除了某个元素只出现一次以外,其余每个元素均出现两次"。

这意味着,如果我们把数组里所有的数字都拿来一起做异或运算:

  • 那些成对出现的数字(比如两个 2,两个 1),会因为归零律a ^ a = 0)全部互相抵消变成 0。
  • 剩下的那个只出现一次的数字,会因为恒等律a ^ 0 = a),和一堆 0 异或后,依然保留它原本的值。

再加上交换律,无论这些数字在数组里排得多乱,我们都可以脑补成把所有相同的数字挪到一起先抵消掉。

java 复制代码
public int singleNumber(int[] nums) {
    int res = 0; // 初始值为0,因为 0 异或任何数都不改变它
    for(int i = 0; i < nums.length; i++) {
        res = res ^ nums[i]; // 把所有数字串起来做异或
    }
    return res; // 成对的都抵消成0了,只剩下那个唯一的数字
}

复杂度分析

  • 时间复杂度:O(N)。只需要把数组从头到尾遍历一遍。
  • 空间复杂度 :O(1)。全程只用了一个变量 res 来存结果,没有开辟任何额外的数组或哈希表,完美符合题目要求的"常量额外空间"。

但是我将入参数组改为:

复制代码
int[] nums = {4,4,1,2,1,2,2,3};

跑出来的结果是1,跑去问通义,给出的回答是:

这里其实藏着一个小陷阱,是因为测试用的数组并不符合这道算法题的"前置条件"。

有一个非常严格的前提:"除了某个元素只出现一次以外,其余每个元素均出现两次。"

也就是说,这个算法能直接得出"那个唯一的单身狗",是建立在其他所有干扰项都必须是成双成对(偶数次)的基础上的。而你的测试数组里,数字 2 出现了 3 次,打破了这个规则,所以最后剩下的就不是单纯的某一个原数组里的数了,而是 2 和 3 异或后的混合产物。

就这?我继续追问"这个算法对数据的要求挺高,现实中怎么可能成对出现呢"

哈哈,你这一针见血!确实,现实世界的数据要是都像力扣题目里这样"成双成对",那月老都得失业了。

不过,虽然现实数据不会乖乖地"成对出现",但这个异或算法在真实的工程开发中,其实有一个超级经典且硬核的应用场景------校验数据的完整性(找不同)。

假设你在用网盘或者聊天软件传一个超大的文件(比如一个 2GB 的电影)。网络传输过程中难免会丢包或者出错,你怎么保证对方收到的文件和原文件一模一样呢?

这时候就可以用到异或的思想(进阶版叫 CRC 校验):

发送方:把整个大文件的所有字节数据做一遍异或(或者更复杂的校验运算),得出一个小小的"指纹"(校验码)。

接收方:收到文件后,也用同样的方法把所有字节异或一遍,算出自己的"指纹"。

对比:如果两个"指纹"不一样,说明文件在传输过程中被篡改或者损坏了,需要重新传。

在这个场景下,只要两边的数据完全一致,所有的字节异或下来都会完美抵消;一旦中间混进了哪怕一个字节的"杂音"(不一样的数据),最终的结果就会天差地别。这就是异或运算在底层通信、分布式存储(比如 RAID 磁盘阵列的容错备份)里的真实威力。

算法题的意义:

所以,刷算法题的时候,咱们可以换个角度来看待这些"苛刻的条件":

它们其实是在人为地创造一个极端的环境,逼着我们去发现某种数据结构或数学规律(比如位运算、单调栈、前缀和等)的极致特性。

做题时:我们学习的是这种"屠龙技"(特定的技巧和思维)。

工作时:现实中虽然没有现成的"龙"(完美的成对数据),但当我们遇到类似"海量数据找差异"、"极速比对"的需求时,脑子里蹦出的那个"异或"的念头,就是当初刷题留下的肌肉记忆。

#算法 #职场 #力扣

相关推荐
灵智实验室2 小时前
PX4状态估计技术EKF2详解(六):EKF2 磁力计融合——从航向修正到 3D 姿态约束
算法·无人机·px 4
JieE2122 小时前
手把手带你用虚拟头节点实现单链表,搞定所有边界问题
javascript·算法
一切皆是因缘际会2 小时前
从概率拟合到内生心智:七层投影架构重构AGI数字生命新范式
大数据·数据结构·人工智能·重构·架构·agi
历程里程碑2 小时前
56 . 高效ET非阻塞IO服务器设计指南
java·运维·服务器·开发语言·数据结构·c++·排序算法
搞科研的小刘选手3 小时前
【大连市计算机学会主办】第三届图像处理、智能控制与计算机工程国际学术会议(IPICE 2026)
图像处理·人工智能·深度学习·算法·计算机·数据挖掘·智能控制
南境十里·墨染春水3 小时前
数据结构 —— 顺序表
数据结构
人月神话-Lee3 小时前
【图像处理】高斯模糊——最优雅的模糊算法
图像处理·人工智能·算法·ios·ai编程·swift
tongluowan0073 小时前
数据结构 Bitmap(位图)示例 - 用户签到系统
开发语言·数据结构·bitmap·用户签到系统
洛水水3 小时前
Redis对象类型与底层数据结构
数据结构·数据库·redis