学习笔记:找出班级中唯一不重复的数字
问题描述
在一个班级中,每位同学都拿到了一张卡片,上面有一个整数。已知除了一个数字之外,所有的数字都恰好出现了两次。我们的任务是设计一个算法来找到那个唯一没有重复的数字。
样例分析
样例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 ≤ 1001
- 0 ≤ cards[i] ≤ 1000
- 班级人数为奇数
- 除了一个数字卡片只出现一次外,其余每个数字卡片都恰好出现两次
解题思路
1. 时间复杂度要求 O(n)
题目要求时间复杂度为 O(n),这意味着我们不能使用如嵌套循环的 O(n²) 复杂度解决方案。
2. 尽量减少空间使用
尽量减少额外空间的使用,意味着我们希望在不使用额外的存储空间或使用很少的空间的前提下完成任务。
3. 巧用位运算 XOR
我们可以通过位运算中的 异或运算 (XOR) 来解决这个问题。异或运算具有以下几个重要特性:
- 任意数与自身异或结果为 0,即 a ^ a = 0
- 任意数与 0 异或结果为该数本身,即 a ^ 0 = a
- 异或运算具有交换律和结合律,即 (a ^ b) ^ c = a ^ (b ^ c)
基于以上特性,我们可以做以下推理:
- 如果数组中每个数字都出现两次,那么相同的数字异或结果为 0(如:a ^ a = 0)。
- 唯一没有配对的数字将保留在最终的异或结果中。
4. 算法设计
1. 初始化一个变量 result = 0,用于保存异或的结果。
2. 遍历数组中的每个数字,将其与 result 进行异或操作。
3. 遍历结束后,result 中保存的就是那个唯一没有配对的数字。
5. 代码实现
def find_unique_number(cards):
result = 0
for num in cards:
result ^= num
return result
6. 运行原理
- 假设数组为 cards = [1, 1, 2, 2, 4],我们依次异或每个数字:
1. result = 0 ^ 1 = 1
2. result = 1 ^ 1 = 0(1 和 1 异或结果为 0)
3. result = 0 ^ 2 = 2
4. result = 2 ^ 2 = 0(2 和 2 异或结果为 0)
5. result = 0 ^ 4 = 4
最终 result = 4,即唯一不重复的数字是 4。
7. 时间和空间复杂度分析
- 时间复杂度:由于我们只遍历数组一次,时间复杂度为 O(n),其中 n 是数组的长度。
- 空间复杂度:我们只使用了一个额外的变量 result,因此空间复杂度为 O(1)。
心得与体会
这道题目让我深刻理解了位运算的妙用。异或运算不仅高效,而且能够巧妙地解决很多与数字配对相关的问题。通过这道题,我学会了如何在不使用额外空间的情况下,利用异或运算找到唯一出现一次的数字。
相比于传统的哈希表或计数方式,异或法在空间优化上有显著优势。而且它的时间复杂度是线性的,非常符合题目的要求。通过这次学习,我对位运算在算法中的应用有了更深刻的认识,也体会到了算法优化的重要性。
拓展思考
异或运算还有很多其他的应用场景,例如:
- 交换两个数而不需要临时变量。
- 在加密算法中的应用。
- 查找数组中两个不重复的数字。
这道题不仅仅让我解决了特定的问题,更重要的是让我意识到了基础运算符在算法设计中的巨大潜力。