题目解析
在班级发放数字卡片这个场景中,我们面临一个独特的算法问题:如何在一组数字中找出唯一一个只出现一次的数字。这个问题虽然看似简单,但要在满足时间复杂度O(n)和最小空间复杂度的约束下实现最优解,需要仔细思考和巧妙的设计。
关键约束
从题目中可以提取以下约束条件:
- 1 ≤ cards.length ≤ 1001 (数组长度限制)
- 0 ≤ cards[i] ≤ 1000 (卡片上的数字范围)
- 班级人数为奇数(因为只有一个数字出现一次,其他都出现两次)
- 除了一个数字出现一次外,其他数字都出现两次
解题思路
-
方法一:哈希表统计
- 遍历数组统计每个数字出现的次数
- 找出只出现一次的数字
- 时间复杂度 O(n),空间复杂度 O(n)
-
方法二: 排序后比较法
- 思路:先排序,然后比较相邻元素
- 优点:无需额外空间
- 缺点:时间复杂度为O(nlogn),不满足要求
-
方法三:异或运算(最优解)
-
利用异或运算的特性:
- a ⊕ a = 0
- a ⊕ 0 = a
- 满足交换律和结合律
-
对数组所有元素进行异或运算
-
最终结果就是只出现一次的数字
-
时间复杂度 O(n),空间复杂度 O(1)
-
代码实现(选用最优解)
#include <iostream>
#include <vector>
int solution(std::vector<int> cards) {
// 用于存储异或结果
int result = 0;
// 遍历数组中的每个数字
for (int num : cards) {
// 利用异或运算的特性:
// 1. a⊕a=0:相同的数异或结果为0
// 2. a⊕0=a:任何数与0异或等于它本身
// 3. 异或运算满足交换律和结合律
result ^= num;
}
// 最终结果就是只出现一次的数字
return result;
}
代码详解
-
初始化:
- 创建变量
result
并初始化为0,用于存储异或运算的结果
- 创建变量
-
核心算法:
- 遍历数组中的每个数字
- 将每个数字与 result 进行异或运算
- 由于相同的数字异或为0,最终只出现一次的数字会保留下来
-
返回结果:
- 返回 result,即为只出现一次的数字
算法正确性证明
以示例 [1,1,2,2,3,3,4,5,5]
为例:
- result = 0
- 0⊕1 = 1
- 1⊕1 = 0
- 0⊕2 = 2
- 2⊕2 = 0
- 0⊕3 = 3
- 3⊕3 = 0
- 0⊕4 = 4
- 4⊕5 = 4⊕5
- (4⊕5)⊕5 = 4
最终得到只出现一次的数字 4。
复杂度分析
- 时间复杂度:O(n),其中 n 是数组长度
- 空间复杂度:O(1),只使用了常数额外空间