只出现一次的数字:异或一把梭 vs HashMap 计数(两种解法完整复盘)
题目给一个非空整数数组 nums:
除了某个元素只出现一次外,其余每个元素都出现两次。要求找出那个只出现一次的元素。
并且有两个硬要求:
- 时间复杂度:
O(n) - 额外空间:
O(1)(常量空间)
这两个条件决定了解法的优先级:能满足 O(1) 空间的只有位运算这条路最合适。当然,用 HashMap 计数也能过,但属于"能做但不符合空间约束"的思路。
解法一:异或 XOR(满足题目所有限制,最佳解)
代码
java
class Solution {
public int singleNumber(int[] nums) {
int res = 0;
for(int x:nums){
res ^= x;
}
return res;
}
}
关键思路:把数组所有数异或起来
异或(XOR,记作 ^)有三个非常重要的性质:
a ^ a = 0(相同的数异或为 0)a ^ 0 = a(任何数和 0 异或还是它自己)- 异或满足交换律和结合律:
a ^ b ^ a = (a ^ a) ^ b = 0 ^ b = b
题目里"其余元素都出现两次",这意味着:
- 每个出现两次的数,最终都会在异或中"互相抵消"变成 0
- 只出现一次的那个数没有配对,最后会"剩下来"
所以把所有数从头到尾异或一遍,最终结果就是答案。
用示例走一遍(直觉会更牢)
[4,1,2,1,2]
res = 0res ^= 4→ 4res ^= 1→ 4^1res ^= 2→ 412res ^= 1→ 4(11)^2 = 402 = 4^2res ^= 2→ 4(22) = 4^0 = 4
答案就是 4。
复杂度
- 时间:
O(n)(一次遍历) - 空间:
O(1)(只用了一个整数 res)
这个解法为什么"非常干净"
因为它不关心数值大小、正负、顺序,只依赖"出现两次就抵消"的结构。题目条件越严格,这种解法越显得漂亮。
解法二:HashMap 计数(通用但不满足 O(1) 空间)
代码
java
class Solution {
public int singleNumber(int[] nums) {
HashMap<Integer,Integer> hashnums = new HashMap<>();
for(int i = 0;i<nums.length;i++){
hashnums.put(nums[i],hashnums.getOrDefault(nums[i],0) + 1);
}
for(int i = 0;i<nums.length;i++){
if(hashnums.get(nums[i]) == 1) return nums[i];
}
return -1;
}
}
关键思路:统计每个数字出现次数
这条路的思路非常直观:
- 第一遍遍历:用 HashMap 记录每个数字出现的次数
- 第二遍遍历:找到计数为 1 的那个数字并返回
这里用到了一个很实用的 API:
getOrDefault(key, 0):如果 key 不存在,就当它出现过 0 次V getOrDefault(Object key, V defaultValue)返回 key 对应的 value,key 不存在,返回默认值- 所以这里的意思是,如果Key不存在,就初始化其value为0+1;如果Key存在,其value为原值+1
- 与本专栏的Leetcode387一个道理
复杂度
- 时间:两次遍历都是
O(n),总的还是O(n) - 空间:最坏情况下 HashMap 需要存所有不同数字,空间
O(n)
这条路的优点
- 思路直接、可扩展:
如果题目改成"出现一次,其余出现三次/五次/不固定次数",HashMap 基本都能通吃。 - 读代码非常直观,不需要位运算基础。
这条路的缺点
- 不满足题目要求的"常量额外空间"
- 还有装箱开销(int → Integer),HashMap 本身也有较大的常数开销
两种解法对比(从多个维度看差异)
1)是否满足题目硬性要求
- 异或:✅
O(n)时间 + ✅O(1)空间(完全满足) - HashMap:✅
O(n)时间 + ❌O(n)空间(不满足)
2)性能与常数开销
- 异或:只做整数运算,CPU 级别快,内存压力极小
- HashMap:哈希计算、扩容、对象装箱、内存访问更重,常数开销明显更大
3)可扩展性
- 异或:只适用于"其他都出现两次"的结构(非常精准)
- HashMap:更通用,条件一变也能继续用
4)表达题目结构的能力
- 异或:把"成对出现抵消"的数学结构直接写进代码里,属于"对症下药"
- HashMap:属于"统计学做法",能做但有点"用力过猛"
总结
这题最推荐的写法就是异或:
- 一次遍历
- 常量空间
- 代码短到几乎没有出错空间
- 完全契合题目约束
HashMap 解法也能正确输出答案,但它的角色更像是"通用备胎":当题目不要求 O(1) 空间,或者题目条件更复杂时,它会更顺手;而在本题这种强约束场景下,异或才是标准答案。
如果想进一步进阶,可以顺着这题继续看两个经典变体:
- "除了一个数出现一次,其余出现三次"
- "两个数出现一次,其余出现两次"
它们都是位运算体系里非常漂亮的延伸。