问题描述
在一个班级里,每位学生都得到了一张写有整数的卡片。这些整数中,除了一个独特的数字外,其余的数字都恰好出现了两次。现在,班长小C需要找出那个持有独特数字卡片的同学。
要求
- 设计一个时间复杂度为O(n)的算法,其中n代表班级人数(也即卡片的数量)。
- 尽量减少额外空间的使用,展示算法优化能力。
测试样例
-
样例1
- 输入:
cards = [1, 1, 2, 2, 3, 3, 4, 5, 5] - 输出:
4 - 解释:数字4是唯一没有配对的数字。
- 输入:
-
样例2
- 输入:
cards = [0, 1, 0, 1, 2] - 输出:
2 - 解释:数字2是唯一只出现一次的数字。
- 输入:
-
样例3
- 输入:
cards = [7, 3, 3, 7, 10] - 输出:
10 - 解释:10是班级中唯一不重复的数字。
- 输入:
约束条件
1 ≤ cards.length ≤ 10010 ≤ cards[i] ≤ 1000- 班级人数为奇数(确保有一个独特数字)
- 除了一个数字卡片只出现一次外,其余每个数字卡片都恰好出现两次
问题核心
在一个由整数构成的卡片集合中,除了一个数字是唯一的(即只出现一次)之外,其余的数字都恰好成对出现两次。我们的任务是找出这个唯一的数字。
解决方案概述
为了找到这个唯一的数字,我们可以巧妙地利用异或(XOR)操作的数学特性。异或操作具有以下几个关键特性:
- 自反性:对于任意整数
a,有a XOR a = 0。 - 零元性:对于任意整数
a,有a XOR 0 = a。 - 交换律:
a XOR b = b XOR a。 - 结合律:
(a XOR b) XOR c = a XOR (b XOR c)。
这些特性让我们能够通过将所有数字进行异或操作来找出唯一的数字,因为成对的数字会在异或过程中相互抵消。
算法详细步骤
-
初始化:我们设置一个变量
res(代表结果)并初始化为0。 -
遍历与异或:遍历卡片集合中的每个数字
x,将res与x进行异或操作,并将结果存回res中。- 即执行
res = res XOR x。
- 即执行
-
返回结果:遍历结束后,
res中存储的就是那个唯一的数字,将其作为函数返回值。
示例解析
以cards = [1, 1, 2, 2, 3, 3, 4, 5, 5]为例:
-
初始时,
res = 0。 -
遍历并异或所有数字后:
res = 0 XOR 1 = 1res = 1 XOR 1 = 0res = 0 XOR 2 = 2res = 2 XOR 2 = 0res = 0 XOR 3 = 3res = 3 XOR 3 = 0res = 0 XOR 4 = 4res = 4 XOR 5 = 1res = 1 XOR 5 = 4(此时,所有成对数字都已抵消,仅剩唯一的4)
-
最终,
res = 4,这就是我们要找的唯一数字。
算法复杂度分析
- 时间复杂度:O(n),因为我们只需遍历一次卡片集合。
- 空间复杂度:O(1),因为我们只使用了一个额外的变量
res来存储异或结果。
代码实现
def solution(cards):
# 初始化一个变量res为0,用于存储异或结果
res = 0
# 遍历cards列表中的每个元素x
for x in cards:
# 对res和x执行异或操作,结果存回res
res = res ^ x
# 返回最终的异或结果,即那个只出现一次的数字
return res
if __name__ == "__main__":
print(solution([1, 1, 2, 2, 3, 3, 4, 5, 5]) == 4) # 预期输出: True
print(solution([0, 1, 0, 1, 2]) == 2) # 预期输出: True
这段代码实现了上述算法,并通过测试用例验证了其正确性。
总结
利用异或操作的数学特性,我们能够高效地找出在一个由成对出现和一个唯一出现的数字构成的集合中的那个唯一数字。这种方法不仅时间复杂度低(O(n)),而且空间复杂度也极低(O(1)),非常适用于处理大规模数据集。