找单独的数
题目描述
在一个班级中,每位同学都拿到了一张卡片,上面有一个整数。有趣的是,除了一个数字之外,所有的数字都恰好出现了两次。现在需要你帮助班长小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 ≤ 1001
- 0 ≤ cards[i] ≤ 1000
- 班级人数为奇数
- 除了一个数字卡片只出现一次外,其余每个数字卡片都恰好出现两次
思路
这道题初看很简单,但是思路解法很多,题目就是要找到数字卡片中唯一出现一次的数,难度在于如何“高效”的找出他
思路 1: 异或运算
基本原理:
-
使用异或运算的性质。对于任何数字
x,有以下特点:x ^ x = 0(任何数与自己异或为 0)x ^ 0 = x(任何数与 0 异或为该数本身)- 异或操作是可交换和结合的,即
a ^ b = b ^ a,a ^ (b ^ c) = (a ^ b) ^ c。
实现步骤:
- 初始化一个变量
unique为 0。 - 遍历数组中的每个数字,将其与
unique进行异或运算。 - 最后
unique中的值即为只出现一次的那个数字。
时间复杂度:O(n)
空间复杂度:O(1)
思路 2: 使用 HashMap
基本原理:
- 利用哈希表(HashMap)来统计每个数字出现的次数。
实现步骤:
- 创建一个空的 HashMap。
- 遍历数组,将每个数字作为键,出现的次数作为值更新到 HashMap 中。
- 遍历 HashMap,找到出现次数为 1 的键。
时间复杂度:O(n)
空间复杂度:O(n)
思路 3: 排序法
基本原理:
- 通过对数组进行排序,然后遍历检查连续的数字。
实现步骤:
- 对数组进行排序(例如使用快速排序或归并排序)。
- 遍历排序后的数组,检查相邻的元素。
- 找到与前后元素都不相等的数字,即为唯一出现的数字。
时间复杂度:O(n log n)(由于排序)
空间复杂度:O(1)(如果使用原地排序)
思路 4: 计数数组
基本原理:
- 当已知数字的范围不大(如 0 到 1000)时,可以使用固定大小的数组进行次数统计。
实现步骤:
- 创建一个大小为 1001 的计数数组(根据题目约束)。
- 遍历输入数组,统计每个数字出现的次数。
- 遍历计数数组,查找出现次数为 1 的索引。
时间复杂度:O(n)
空间复杂度:O(1)(固定大小的数组)
总结与对比
1. 异或运算法
优点:
- 时间复杂度 O(n),只需遍历一次数组,效率非常高。
- 空间复杂度 O(1),不需要额外的存储空间。
缺点:
- 只有在知道所有数字都成对出现的情况下才有效,适用于特定条件。
- 对于不熟悉位运算的开发者,可能理解上存在一些难度。
适用场景:
- 经典的只出现一次的数字问题。
- 需要高效处理大规模数据的场合。
2. HashMap法
优点:
- 逻辑简单明了,易于理解和实现。
- 可以方便地统计同学中多个独特数字的出现次数,只需扩展判断条件即可。
缺点:
- 时间复杂度 O(n),但由于哈希表的开销可能会略微增加常数因子。
- 空间复杂度 O(n),在元素很多时需要较多的存储空间。
适用场景:
- 处理复杂的数据统计问题,可以快速扩展功能。
- 需要多次查找或统计次数的场合。
3. 排序法
优点:
- 方法简单,易于实现并理解。
- 适合有层级关系的数字,使用排序可以直观地找出重复元素。
缺点:
- 时间复杂度 O(n log n),由于排序,效率较低。
- 需要额外的空间(在某些实现中)来存放排序后的数组。
适用场景:
- 当数组的大小处于较小的范围时,可能表现良好。
- 需要额外信息时(如首次出现的位置、频率等)可以考虑。
4. 计数数组法
优点:
- 时间复杂度 O(n),通过计数可以快速查找唯一数字。
- 空间复杂度 O(1)(固定大小),适合处理已知范围的整数。
缺点:
- 仅适用于数字范围已知且较小的情况(如 0 到 1000)。
- 不适合处理负数或超出设定范围的数字。
适用场景:
- 有固定的数字范围且可能出现大量相同数字的情况。
- 整数的范围被限制到某个小范围时表现优秀。
总结对比
| 方法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|---|
| 异或运算法 | O(n) | O(1) | 效率高,空间开销小 | 可能不易理解,适用于特定条件 | 简单的只出现一次的问题 |
| HashMap法 | O(n) | O(n) | 易于理解,可扩展 | 存储开销大,依赖哈希表的特性 | 数据统计,较复杂的事件 |
| 排序法 | O(n log n) | O(1)或O(n) | 实现简单,直观 | 效率低,额外的排序开销 | 需要查找、比较的情况 |
| 计数数组法 | O(n) | O(1) | 快速查找,固定大小的空间 | 仅适用于已知小范围的数字,不支持负数或更多范围条件 | 有固定范围且频率统计的场景 |