1. LeetCode 961题解析:N-Repeated Element in Size 2N Array
这道看似简单的数组题目,实际上考察了我们对基础数据结构的深刻理解和多种解题思路的灵活运用。题目描述为:在一个大小为2N的数组A中,有N+1个不同的元素,其中恰好有一个元素重复了N次,找出这个重复N次的元素。
我第一次遇到这个问题时,直觉告诉我要用哈希表统计频次,但深入思考后发现还有更巧妙的解法。这道题在面试中经常作为热身题出现,主要考察候选人对基础算法的掌握程度和思维灵活性。
2. 题目分析与边界条件
2.1 题目核心理解
题目给出的关键信息有:
- 数组长度为2N
- 有N+1个不同元素
- 只有一个元素重复了N次
- 其他N个元素各出现一次
这意味着数组的结构可以表示为:[x, x, ..., x (共N次), a, b, c,...](共N个不同元素各出现一次)
2.2 边界情况考虑
在实际编码前,我们需要考虑几种特殊情况:
- 最小规模情况:N=1时,数组长度为2,两个相同元素
- 常规情况:如示例中的[1,2,3,3]
- 重复元素连续出现:[3,3,3,1,2,4]
- 重复元素分散出现:[1,3,2,3,4,3]
注意:题目保证输入总是有效的,所以不需要处理无效输入的情况,这在实际面试中也是重要提示。
3. 解法一:哈希表统计法
3.1 基本思路
最直观的解法是使用哈希表(在Python中可以用字典或collections.Counter)统计每个元素的出现次数,当某个元素的计数达到N时返回该元素。
from collections import Counter def repeatedNTimes(A): count = Counter(A) for num in count: if count[num] > 1: return num3.2 复杂度分析
时间复杂度:O(n),需要遍历整个数组一次 空间复杂度:O(n),最坏情况下需要存储n/2+1个不同的元素
3.3 优化思考
虽然这种方法简单直接,但空间复杂度不是最优的。在实际面试中,可以先提出这个解法作为基础,然后思考能否优化空间复杂度。
4. 解法二:数学特性法
4.1 利用数组长度特性
考虑到数组长度为2N,而有N+1个不同元素,其中N个元素只出现一次,我们可以利用这个数学特性:
所有唯一元素的出现次数总和为N,而重复元素出现N次。因此重复元素占总元素的一半。
4.2 实现代码
def repeatedNTimes(A): unique_elements = set() for num in A: if num in unique_elements: return num unique_elements.add(num) return -1 # 根据题目描述,这行实际上不会执行4.3 复杂度分析
时间复杂度:平均O(n),最坏情况下需要遍历整个数组 空间复杂度:O(n),需要存储唯一元素集合
5. 解法三:排序比较法
5.1 排序思路
将数组排序后,重复元素必定会连续出现。对于长度为2N的数组,重复N次的元素要么占据前N个位置,要么占据后N个位置,或者中间有N个连续。
def repeatedNTimes(A): A.sort() for i in range(len(A)-1): if A[i] == A[i+1]: return A[i] return A[-1] # 处理类似[1,2,3,3]的情况5.2 复杂度分析
时间复杂度:O(n log n),主要由排序决定 空间复杂度:O(1)或O(n),取决于排序实现
6. 最优解法:线性扫描与概率分析
6.1 创新思路
经过前面的分析,我们可以发现重复元素在数组中至少会有两个相邻的情况(考虑鸽巢原理)。因此,我们只需要检查每个元素与其相邻元素是否相同即可。
6.2 实现代码
def repeatedNTimes(A): for k in range(1, 4): for i in range(len(A) - k): if A[i] == A[i + k]: return A[i] return -16.3 复杂度分析
时间复杂度:O(n),最多比较3n次 空间复杂度:O(1),不需要额外空间
7. 测试用例设计与验证
7.1 基础测试用例
assert repeatedNTimes([1,2,3,3]) == 3 assert repeatedNTimes([2,1,2,5,3,2]) == 2 assert repeatedNTimes([5,1,5,2,5,3,5,4]) == 57.2 边界测试用例
assert repeatedNTimes([1,1]) == 1 # 最小N assert repeatedNTimes([1,2,3,4,5,5,5,5,5,5]) == 5 # 大N7.3 随机测试用例
可以生成随机数组进行测试,确保算法在各种情况下都能正确工作:
import random def generate_test_case(N): repeated = random.randint(0, 10000) others = random.sample(range(10001), N) # 确保不重复 arr = [repeated] * N + others random.shuffle(arr) return arr, repeated for _ in range(10): N = random.randint(1, 5000) arr, expected = generate_test_case(N) assert repeatedNTimes(arr) == expected8. 实际应用与变种思考
8.1 实际应用场景
这类问题在实际开发中经常出现,比如:
- 日志分析中寻找高频错误码
- 用户行为分析中识别频繁操作
- 数据清洗时检测重复记录
8.2 题目变种思考
如果题目条件变化,我们可以相应调整解法:
- 如果重复次数不固定,但超过一半:可以使用摩尔投票算法
- 如果数组很大且内存有限:可以考虑分块处理
- 如果允许修改原数组:可以通过交换元素等方式实现
8.3 性能对比总结
| 方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| 哈希表统计 | O(n) | O(n) | 通用解法 |
| 集合检测 | O(n) | O(n) | 早期返回可能 |
| 排序比较 | O(n log n) | O(1) | 允许修改原数组 |
| 线性扫描 | O(n) | O(1) | 最优空间复杂度解法 |
在实际面试中,我通常会先提出哈希表解法,然后逐步优化到线性扫描解法,展示思维的全过程。这种从基础到优化的思路展示,往往能给面试官留下更好的印象。