只出现一次的数字
- 只出现一次的数字
- 问题描述
- 示例
- 约束条件
- 解题思路
- 方法一:暴力解法(不满足要求)
- 方法二:位运算(最优解)
- 异或运算的性质
- 解题步骤
- 总结
只出现一次的数字
问题描述
给定一个非空整数数组 nums
,其中除了一个元素只出现一次外,其余每个元素均出现两次。要求找出那个只出现一次的元素。算法需要满足线性时间复杂度 (O(n)) 和常数空间复杂度 (O(1))。
示例
-
输入:
nums = [2, 2, 1]
输出:1
-
输入:
nums = [4, 1, 2, 1, 2]
输出:4
-
输入:
nums = [1]
输出:1
约束条件
- 数组长度范围:
1 <= nums.length <= 3 * 10^4
- 数组元素范围:
-3 * 10^4 <= nums[i] <= 3 * 10^4
解题思路
本题的关键在于如何在不使用额外存储空间的情况下,快速找出只出现一次的数字。以下是几种可能的解法及其优缺点分析:
方法一:暴力解法(不满足要求)
-
使用集合存储数字
遍历数组中的每个数字,如果集合中没有该数字,则将其加入集合;如果集合中已有该数字,则将其从集合中删除。最后剩下的数字即为只出现一次的数字。
缺点:需要额外的 (O(n)) 空间。 -
使用哈希表存储次数
使用哈希表记录每个数字出现的次数,最后遍历哈希表找到只出现一次的数字。
缺点:同样需要额外的 (O(n)) 空间。 -
数学方法(集合和数组和的关系)
计算数组中所有元素的和,以及集合中所有元素的和的两倍。由于集合中元素无重复,因此两倍的集合和减去数组和,即为只出现一次的数字。
缺点:需要额外的 (O(n)) 空间。
方法二:位运算(最优解)
利用异或运算的性质,可以实现线性时间复杂度和常数空间复杂度的解法。
异或运算的性质
- 任何数与 0 异或,结果仍然是该数:
( a \oplus 0 = a ) - 任何数与自身异或,结果为 0:
( a \oplus a = 0 ) - 异或运算满足交换律和结合律:
( a \oplus b \oplus a = b \oplus (a \oplus a) = b \oplus 0 = b )
解题步骤
假设数组中有 ( 2m + 1 ) 个数,其中 ( m ) 个数各出现两次,一个数出现一次。令 ( a_1, a_2, \dots, a_m ) 为出现两次的数,( a_{m+1} ) 为只出现一次的数。根据异或运算的性质,数组中所有元素的异或结果可以表示为:
[
(a_1 \oplus a_1) \oplus (a_2 \oplus a_2) \oplus \dots \oplus (a_m \oplus a_m) \oplus a_{m+1}
]
根据性质 2 和性质 1,上式可以化简为:
[
0 \oplus 0 \oplus \dots \oplus 0 \oplus a_{m+1} = a_{m+1}
]
因此,数组中所有元素的异或结果即为只出现一次的数字。
总结
本题通过利用异或运算的性质,巧妙地实现了线性时间复杂度和常数空间复杂度的解法。这种方法不仅高效,而且简单易懂,是解决类似问题的经典思路。这题只要知道,两个数异或得到0,0与任何数异或得到自己,1与任何数异或得到反数。