青训营刷题笔记:唯一的数字卡片
问题描述
在一个班级中,每位同学都拿到了一张卡片,上面有一个整数。除了一个数字之外,所有的数字都恰好出现了两次。需要帮助班长小C快速找到那个拿了独特数字卡片的同学手上的数字是什么。
要求
- 设计时间复杂度为 O(n)O(n) 的算法,其中 nn 是班级人数。
- 尽量减少额外空间的使用,体现算法优化能力。
解题思路
-
异或操作的特性:
- 对于任意整数 xx,满足 x⊕x=0x \oplus x = 0 和 x⊕0=xx \oplus 0 = x。
- 异或操作满足交换律和结合律,因此一组数字通过异或操作后,相同的数字会两两抵消为 0,最终只剩下唯一一个不重复的数字。
-
时间复杂度和空间复杂度:
- 遍历数组一次完成所有计算,因此时间复杂度为 O(n)O(n)。
- 使用一个变量存储结果,不需要额外空间,空间复杂度为 O(1)O(1)。
算法实现
代码
def solution(cards):
# 初始化结果为0
result = 0
# 遍历卡片列表,并进行异或运算
for card in cards:
result ^= card
# 返回唯一不重复的数字
return result
# 测试用例
if __name__ == "__main__":
print(solution([1, 1, 2, 2, 3, 3, 4, 5, 5]) == 4) # 输出:4
print(solution([0, 1, 0, 1, 2]) == 2) # 输出:2
print(solution([7, 3, 3, 7, 10]) == 10) # 输出:10
测试样例
样例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。
复杂度分析
-
时间复杂度:
- 遍历数组仅需一次,时间复杂度为 O(n)O(n)。
-
空间复杂度:
- 使用常数级别的空间存储异或结果,空间复杂度为 O(1)O(1)。
总结
该解法充分利用了异或操作的性质,实现了时间复杂度为 O(n)O(n) 和空间复杂度为 O(1)O(1) 的高效算法,满足了题目的优化要求。这道题的核心在于找到一个数组中唯一不重复的数字,其余数字均出现两次。解题关键在于使用异或运算的特性,设计出高效且优化的算法。
异或操作特性
- 自反性:x⊕x=0x \oplus x = 0,即相同的数异或为 0。
- 单位元:x⊕0=xx \oplus 0 = x,即一个数与 0 异或不变。
- 交换律和结合律:异或可以改变顺序或分组而结果不变。因此,对于任意序列中成对出现的数字,异或运算会将它们抵消为 0,剩下的唯一一个数字就是答案。
算法设计
通过一次遍历,用一个变量保存异或结果,逐个将数组中的每个数字异或到变量中。最终,这个变量保存的值即为唯一的数字。这种方法时间复杂度为 O(n)O(n),因为只需遍历一次数组;空间复杂度为 O(1)O(1),只使用常量空间存储结果。
测试与优化
测试样例验证了算法在不同情况下的有效性,例如 [1, 1, 2, 2, 3, 3, 4, 5, 5] 的输出为 4,而 [7, 3, 3, 7, 10] 输出为 10,均正确满足题意。
总结
通过异或运算,本算法不仅实现了时间复杂度 O(n)O(n) 和空间复杂度 O(1)O(1),还展现了数学运算在优化算法设计中的重要作用,充分体现了高效和简洁的算法思想。