136. 只出现一次的数字 (Easy)

专题归类: 12-技巧 LeetCode 链接: https://leetcode.cn/problems/single-number/


在线做题: 写 Python 代码并运行 · Java 跳转 LeetCode

题目描述

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

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

示例 1:

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

示例 2:

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

示例 3:

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

题目详细分析

数据范围: 1 <= nums.length <= 3 * 10^4-3 * 10^4 <= nums[i] <= 3 * 10^4

核心约束:

  • 除一个元素外,其他元素均恰好出现两次——这是关键性质
  • 必须 O(n) 时间 + O(1) 空间,排除了哈希表/排序等方案
  • 元素可能为负数

关键洞察:

  • 题目对时间和空间有严格要求,暗示需要一个数学/位运算技巧
  • “出现两次”和”只出现一次”——这个描述强烈暗示**异或(XOR)**运算
  • 异或的性质:a ^ a = 0a ^ 0 = a,且异或满足交换律和结合律
  • 将所有数字异或,成对出现的会抵消为 0,剩下的就是答案

小白版直白理解

就像在一个派对上,大家都成双成对地跳舞,只有一个人是单身。你想快速找出那个单身的人。

最笨的办法是拿个名单,每个人来了就记一笔,最后看谁只出现一次——但这需要额外记名单(哈希表)。

有一个聪明的方法:想象每个数字是一盏灯,碰见相同的数字两次,灯就熄灭(归零)。那么从头到尾把所有数字过一遍,相同的数字会互相抵消,最后剩下的就是那个单身汉。这就像”连连看”的消除游戏——相同的两个碰到一起就消失,最后剩下的就是答案。


解题思路

思路一:异或运算(推荐)

思路讲解: 利用异或运算的三个性质:

  1. a ^ a = 0 — 相同数字异或结果为 0(自反性)
  2. a ^ 0 = a — 任何数与 0 异或等于自身
  3. 异或满足交换律和结合律:a ^ b ^ c = a ^ c ^ b

因此,把所有数字异或起来:成对出现的数字异或结果为 0,0 再与单身的那个数字异或,结果就是它本身。

def singleNumber(nums):
    ans = 0
    for num in nums:
        ans ^= num  # 所有数异或,成对的消为 0,剩下的就是答案
    return ans

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

思路二:数学法(集合运算)

思路讲解: 如果时间要求不那么严格,可以用数学技巧:2 * sum(set(nums)) - sum(nums)。将所有不同元素的和乘以 2,减去实际数组的和,差就是只出现一次的元素。这需要 O(n) 额外空间存储集合。

def singleNumber(nums):
    return 2 * sum(set(nums)) - sum(nums)

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

思路三:哈希表计数

思路讲解: 用哈希表统计每个数字出现的次数,再找出出现次数为 1 的数字。虽然直观,但不符合 O(1) 空间要求。

def singleNumber(nums):
    count = {}
    for num in nums:
        count[num] = count.get(num, 0) + 1
    for num, cnt in count.items():
        if cnt == 1:
            return num

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


易错点

  • 只有一个元素:如 nums = [1],异或结果为 1,代码应正确处理
  • 负数的情况:异或运算在 Python 中对负数也能正常工作,无需特殊处理
  • 不要忘记初始化 ans = 0:如果初始化为其他值,结果会出错
  • 异或运算在 Python 中优先级低于比较运算符:如果混合使用需要加括号,但本题中只需连续异或,无需担心
  • sum(set(nums)) 可能溢出:虽然 Python 整数无上限,但其他语言需要注意
  • 题目保证只有 1 个单身数:如果有多个或没有,异或法就不适用了

框架提炼

异或技巧模板:找出现奇数次的元素

def findOdd(nums):
    ans = 0
    for num in nums:
        ans ^= num
    return ans

核心原理: 异或运算具有自反性a ^ a = 0),适合解决”配对消除”类问题。

扩展应用:

  • 找出现奇数次的数字(不一定是 1 次,3 次、5 次等奇数次都能用异或解决)
  • 交换两个数a ^= b; b ^= a; a ^= b
  • 找两个只出现一次的数(见 260 题):先整体异或得到 x ^ y,再根据某一位分组
  • 判断一个数是不是 2 的幂n > 0 and (n & (n - 1)) == 0

位运算常用技巧:

运算效果
x ^ x0
x ^ 0x
x & (-x)取最低位的 1
x & (x - 1)消除最低位的 1

关联题目