当前位置: 首页 > news >正文

LeetCode-136:只出现一次的数字,三种解法一次讲明白

一、这题表面简单,但题目其实藏了要求

今天这题是 LeetCode 136:只出现一次的数字

题目说:

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

比如:

示例 1

nums = [2, 2, 1]

输出:

1

示例 2

nums = [4, 1, 2, 1, 2]

输出:

4

示例 3

nums = [1]

输出:

1

这题第一眼看上去不难,很多人会马上想到:

  • 要不一个个数次数
  • 要不比较谁只出现了一次

这些思路都可以做。

但题目还有一句很关键的话:

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

所以这题不只是“做出来”,还在考你:

  • 能不能做到 O(n) 时间
  • 能不能做到 O(1) 额外空间

也正因为这样,这题特别适合写成“三版答案”来理解。


二、先说结论:这题有三层学法

我建议你把这题分成三层来学。

第一版:暴力法

最直观,最容易想到,但效率一般。

第二版:哈希表计数

逻辑清楚,代码也不难,时间复杂度达标,但空间不满足最优要求。

第三版:异或

这是这题真正想让你掌握的核心技巧,时间和空间都最优。

下面我们就一版一版来看。


第一版:暴力法

三、最直观的想法:数一数每个数出现了几次

最朴素的思路就是:

对数组里的每个元素,都去整个数组里数一遍它出现了几次。
如果某个数只出现了 1 次,那它就是答案。

比如:

nums = [4, 1, 2, 1, 2]

你可以这样想:

  • 数字 4 出现了几次?1 次
  • 数字 1 出现了几次?2 次
  • 数字 2 出现了几次?2 次

所以答案就是 4


四、暴力法代码

def single_number(nums):for x in nums:if nums.count(x) == 1:return x

五、这版代码怎么理解

这里最关键的是:

nums.count(x)

它的作用是统计 x 在整个列表里出现了多少次。

所以这段代码的意思就是:

  • 依次取出数组中的每个数 x
  • 看它在整个数组里是不是只出现了 1 次
  • 如果是,就返回它

六、暴力法的优缺点

优点

  • 最容易想到
  • 写法也最短
  • 很适合一开始理解题意

缺点

  • 效率不高

因为 for x in nums 已经遍历了一遍数组,
nums.count(x) 每次又要重新扫一遍数组。

所以时间复杂度是:

O(n²)

这不满足题目要求的线性时间复杂度。


第二版:哈希表计数

七、比暴力更自然的优化:用字典统计次数

既然暴力法慢,是因为“每个数都反复数很多次”,那我们就可以换个思路:

用一个哈希表,一次遍历把每个数出现的次数都记下来。

这样就不用反复统计了。

比如:

nums = [4, 1, 2, 1, 2]

遍历完后可以得到:

{4: 1,1: 2,2: 2
}

然后再去找“值为 1”的那个键就行。


八、哈希表版代码

def single_number(nums):hashtable = {}for x in nums:if x in hashtable:hashtable[x] += 1else:hashtable[x] = 1for key in hashtable:if hashtable[key] == 1:return key

九、这版代码逐行讲

1. 创建一个字典

hashtable = {}

用来存“数字 -> 出现次数”的对应关系。


2. 第一次遍历:统计次数

for x in nums:if x in hashtable:hashtable[x] += 1else:hashtable[x] = 1

比如:

nums = [2, 2, 1]

遍历过程是:

  • 看到第一个 2,字典里没有,记成 {2: 1}
  • 看到第二个 2,字典里有,更新成 {2: 2}
  • 看到 1,字典里没有,更新成 {2: 2, 1: 1}

3. 第二次遍历:找次数为 1 的那个数

for key in hashtable:if hashtable[key] == 1:return key

这一步的意思是:

  • 遍历字典里的每个键
  • 如果它对应的计数是 1
  • 那它就是答案

十、哈希表版的优缺点

优点

  • 思路清楚
  • 比暴力法更高效
  • 时间复杂度已经达到 O(n)

缺点

  • 额外开了一个字典
  • 空间复杂度是 O(n)

所以虽然这版很好理解,也能做对题,但它仍然不满足题目“常量额外空间”的最优要求。


第三版:异或

十一、这题真正该学的核心:异或

这题真正经典、也最值得掌握的,是异或 XOR

Python 里异或的符号是:

^

比如:

a ^ b

十二、异或有两个最重要的性质

这题能做出来,靠的就是下面两个性质。

性质 1:一个数和自己异或等于 0

x ^ x = 0

比如:

2 ^ 2 = 0
5 ^ 5 = 0

性质 2:一个数和 0 异或还是它自己

x ^ 0 = x

比如:

7 ^ 0 = 7
1 ^ 0 = 1

十三、为什么异或刚好适合这题

题目说得非常特别:

  • 只有一个数出现一次
  • 其他数都恰好出现两次

这就意味着,如果我们把数组里所有数都异或起来:

例子 1

2 ^ 2 ^ 1

因为:

2 ^ 2 = 0

所以就变成:

0 ^ 1 = 1

最后只剩下 1


例子 2

4 ^ 1 ^ 2 ^ 1 ^ 2

其中:

1 ^ 1 = 0
2 ^ 2 = 0

所以整个式子就等价于:

4 ^ 0 ^ 0 = 4

最后只剩下那个只出现一次的数字 4

所以这题最核心的一句话就是:

相同的数两两异或会抵消,最后剩下的就是那个只出现一次的数。


十四、异或版代码

先看最容易理解的写法:

def single_number(nums):result = 0for x in nums:result ^= xreturn result

十五、这版代码逐行讲

1. 先从 0 开始

result = 0

因为:

x ^ 0 = x

所以从 0 开始是最自然的。


2. 遍历数组,把每个数都异或进去

for x in nums:result ^= x

这句等价于:

result = result ^ x

意思就是不断把所有数字累积异或起来。


3. 最后返回

return result

因为所有成对出现的数都会被抵消,最后 result 里只会剩下那个单独出现一次的数。


十六、手动模拟一遍异或过程

还是看这个例子:

nums = [4, 1, 2, 1, 2]

开始:

result = 0

第一步

result = 0 ^ 4 = 4

第二步

result = 4 ^ 1

第三步

result = 4 ^ 1 ^ 2

第四步

result = 4 ^ 1 ^ 2 ^ 1

因为 1 ^ 1 = 0,这两个 1 抵消。

第五步

result = 4 ^ 2 ^ 2

因为 2 ^ 2 = 0,这两个 2 抵消。

最后只剩:

4

答案就出来了。


十七、官方题解里的 reduce 写法是什么意思

你看到官方题解写的是:

class Solution:def singleNumber(self, nums: List[int]) -> int:return reduce(lambda x, y: x ^ y, nums)

这个本质上和刚才那版 for 循环是一个意思。

reduce 的作用

reduce 会把一个列表里的元素“从左到右不断合并”。

比如:

reduce(lambda x, y: x ^ y, nums)

等价理解为:

(((nums[0] ^ nums[1]) ^ nums[2]) ^ nums[3]) ...

也就是把整个数组一路异或下去。

所以官方这句代码,本质上就是:

把数组里所有元素全部做异或运算

最终结果自然就是那个只出现一次的数字。


十八、reduce 版代码

如果你想写成官方题解风格,需要先导入:

from functools import reduce

完整写法是:

from functools import reducedef single_number(nums):return reduce(lambda x, y: x ^ y, nums)

十九、三版答案放在一起对比

版本 1:暴力法

def single_number(nums):for x in nums:if nums.count(x) == 1:return x

时间复杂度:

O(n²)

空间复杂度:

O(1)

特点:

  • 最直观
  • 但太慢

版本 2:哈希表

def single_number(nums):hashtable = {}for x in nums:if x in hashtable:hashtable[x] += 1else:hashtable[x] = 1for key in hashtable:if hashtable[key] == 1:return key

时间复杂度:

O(n)

空间复杂度:

O(n)

特点:

  • 很好理解
  • 时间达标
  • 但空间不是最优

版本 3:异或

def single_number(nums):result = 0for x in nums:result ^= xreturn result

或者:

from functools import reducedef single_number(nums):return reduce(lambda x, y: x ^ y, nums)

时间复杂度:

O(n)

空间复杂度:

O(1)

特点:

  • 最优解
  • 也是这题真正想考的点

二十、这题本质上在练什么

这题表面上是“找一个数”,实际上它在练你三层能力。

第一层:能不能先做出来

暴力法就属于这一层。

第二层:能不能优化到线性时间

哈希表法就属于这一层。

第三层:能不能利用题目特殊性质,做到时间和空间都最优

异或法就属于这一层。

所以这题很适合作为“从直觉思路,走到位运算技巧”的入门题。


二十一、写给今天刚学这题的你

如果你第一次做这题,先想到哈希表,其实已经很好了。

因为这说明你已经抓住了:

要先统计出现次数

而当你继续往下看到异或解法,就会开始接触一种更高级但很常见的刷题思维:

题目给出的特殊条件,往往就是最优解的入口。

这题里那个最关键的条件就是:

  • 其他数都出现两次
  • 只有一个数出现一次

这正好和异或的“成对抵消”性质完美匹配。

所以这题真正值得带走的,不只是答案,而是这种“条件和性质对应起来”的感觉。


二十二、小结

这道题最核心的一句话就是:

相同的数异或会抵消成 0,最后剩下的就是那个只出现一次的数字。

所以三版答案里:

  • 暴力法最直观
  • 哈希表法最容易理解
  • 异或法才是最优解

如果你现在已经看懂了异或为什么能做这题,那这题你就不只是“会做”,而是真的“学会了”。


结尾:这题是位运算入门的好题

很多人一看到“异或”就下意识害怕,觉得位运算很抽象。
但这题其实是一个特别好的入门点。

因为它让你看到:

  • 位运算不只是公式
  • 它真的可以把题目做得更简洁、更漂亮
  • 有些最优解,正是来自这些看起来很小众的性质

所以今天这题很值得多看一眼。

不是为了死记 ^ 这个符号,
而是为了记住这种感觉:

当题目里有“成对出现、只剩一个不同”的结构时,就可以想想异或。

http://www.jsqmd.com/news/493743/

相关文章:

  • 【图像加密】基于Shuffling 和 Diffusion算法进行图像加密附matlab代码
  • 程序员如何应对“35岁危机”?
  • 2026年热门的集成吊顶公司推荐:集成吊顶蜂窝大版直销厂家推荐 - 品牌宣传支持者
  • mysql之数字函数
  • JavaWeb开发:Servlet核心技术全解析
  • 三机九节点电力系统 Simulink 仿真模型探索
  • 精仪智检:科创驱动下的智慧海洋监测体系构建与产业化实践
  • C++的std--unreachable:标记不可能到达的代码路径
  • MySQL输入密码后闪退?
  • 【数据分析】基于MATLAB的分数阶Calderón问题的马尔可夫链蒙特卡罗(MCMC)算法实现
  • 软件设计师-上下文无关文法
  • 人工智能应用- 天文学家的助手:06. 检测射电频率干扰
  • 新手入门模拟IC设计之锁相环PLL电路探秘
  • 流程图在线工具 https://app.diagrams.net/
  • WW2文本分析:基于规则的军事命名实体识别
  • C++哈希表封装实战指南
  • Elastic 的 Agent 技能:让你的 AI 代理成为 Elastic 专家
  • Youtu-VL-4B-Instruct-GGUF模型效果深度评测:多模态指令跟随能力展示
  • 毕设程序java社区公益图书借阅系统设计 基于Java的社区共享图书流通平台开发 智慧社区图书互助服务系统的设计与实现
  • 基于python的小说在线阅读平台 数据可视化 章节
  • PostgreSQL MCP Server:让 AI 直接读懂你的数据库
  • OpenClaw(小龙虾)详细介绍与Windows安装教程
  • 定制抗体服务为何成为前沿生物医学研究的关键支撑?
  • 【跟韩工学Ubuntu第1课】 第1章 系统架构、启动流程与内核管理-006篇-本章练习题
  • 【那片果园,和看不见的根】
  • 《AI是如何”预见”Oracle安装中的错误的?》
  • 射频实验室生存法则:资深工程师的避坑指南
  • 【LVDS电路结构】
  • 基于深度神经网络(RNN + LSTM)的分类模型探索
  • 家用路由器不仅可以上网,还可以玩这6件事