题目解析
在班级中的 "独特卡片数字" 问题要求找到唯一只出现一次的数字,其余所有数字都出现了两次。这是一个典型的 "异或" 问题,利用了异或运算的性质,结合时间复杂度 O(n) 和空间复杂度 O(1) 的要求,设计算法。
题目分析
-
输入输出:
- 输入是一维整数数组
cards
,长度为奇数,满足 ( 1 \leq \text{cards.length} \leq 1001 )。 - 输出为唯一只出现一次的数字。
- 输入是一维整数数组
-
约束条件:
- 数组中除了一个数字,其余数字均恰好出现两次。
- 每个数字的取值范围为 ( 0 \leq \text{cards[i]} \leq 1000 )。
-
数学背景:
- 异或运算
^
具有以下重要性质:- ( a \oplus a = 0 ) (任意数异或自身结果为 0)。
- ( a \oplus 0 = a ) (任意数异或 0 结果为自身)。
- 异或满足交换律与结合律:( a \oplus b \oplus c = (a \oplus c) \oplus b )。
- 如果将数组中所有数字异或,成对出现的数字会相互抵消,最终结果即为只出现一次的数字。
- 异或运算
解题思路
本题的关键在于利用异或的特性简化问题:
- 初始化结果变量
result = 0
。 - 遍历数组,将每个数字依次与
result
异或。 - 遍历结束时,
result
中存储的即为唯一出现一次的数字。
算法复杂度
- 时间复杂度:
- 遍历数组一次,时间复杂度为 O(n)。
- 空间复杂度:
- 只使用一个额外变量
result
,空间复杂度为 O(1)。
- 只使用一个额外变量
图解
假设输入数组为 [1, 1, 2, 2, 3, 3, 4, 5, 5]
,我们通过异或操作依次更新 result
的值:
步骤 | 当前数字 | 当前 result | 计算过程 |
---|---|---|---|
初始 | - | 0 | - |
1 | 1 | 1 | ( 0 \oplus 1 ) |
2 | 1 | 0 | ( 1 \oplus 1 ) |
3 | 2 | 2 | ( 0 \oplus 2 ) |
4 | 2 | 0 | ( 2 \oplus 2 ) |
5 | 3 | 3 | ( 0 \oplus 3 ) |
6 | 3 | 0 | ( 3 \oplus 3 ) |
7 | 4 | 4 | ( 0 \oplus 4 ) |
8 | 5 | 1 | ( 4 \oplus 5 ) |
9 | 5 | 4 | ( 1 \oplus 5 ) |
最终结果为 4。
代码实现
# 方法:异或法
def solution(cards):
result = 0
for num in cards:
result ^= num # 将当前数字与结果异或
return result
# 测试用例
if __name__ == "__main__":
# 示例1
assert solution([1, 1, 2, 2, 3, 3, 4, 5, 5]) == 4
# 示例2
assert solution([0, 1, 0, 1, 2]) == 2
# 示例3
assert solution([7, 3, 3, 7, 10]) == 10
print("所有测试用例通过!")
拓展思考
异或法的优雅之处在于它不仅解决了本题,还可以用于其他类似问题,比如:
- 找数组中两个不重复的数字:
- 如果数组中有两个不重复的数字,则需要分组异或以区分它们。
- 判断数组是否存在重复数字:
- 将所有数字和从 ( 1 ) 到 ( n ) 的自然数异或,若结果不为 0,说明存在重复。
思考
-
使用位运算的优越性:
- 相比哈希表法,位运算省去了额外的空间。
- 在硬件级别,异或操作非常高效,适合处理大规模数据。
-
可扩展性:
- 如果输入数据中数字范围增大(如 ( 10^9 ) 级别),异或法仍然适用,而哈希表可能因内存消耗过高而失效。
-
注意边界条件:
- 输入数组长度为 1 的情况,直接返回第一个数字。
- 数据为空时(理论上不会出现),需添加额外的错误处理逻辑。
本题通过数学特性优化代码的重要性,将问题从一般的时间复杂度 O(n^2) 简化到 O(n),并通过精简操作达成最优解。