1. 题目解析与核心思路
这道LeetCode 287题"寻找重复数"属于数组类中等难度题目,题目要求在一个包含n+1个整数的数组nums中,找出那个重复的数字。题目给出了两个关键限制条件:
- 数组中的数字范围在[1, n]之间
- 有且只有一个重复数字,但该数字可能重复多次
1.1 暴力解法与哈希表
最直观的解法是使用双重循环暴力搜索,时间复杂度O(n²),这在LeetCode上显然会超时。进阶一点的解法是使用哈希表记录已出现的数字,时间复杂度O(n),但空间复杂度也是O(n)。
python复制def findDuplicate(nums):
seen = set()
for num in nums:
if num in seen:
return num
seen.add(num)
1.2 二分查找的巧妙应用
更优的解法是使用二分查找,时间复杂度O(nlogn),空间复杂度O(1)。这个解法的核心思想是:对于数字1到n,统计数组中<=当前数字的个数。如果个数>当前数字,说明重复数字在左半区。
python复制def findDuplicate(nums):
left, right = 1, len(nums)-1
while left < right:
mid = (left + right) // 2
count = sum(num <= mid for num in nums)
if count > mid:
right = mid
else:
left = mid + 1
return left
注意:这种解法利用了题目中数字范围在[1,n]的特性,将原本用于有序数组的二分查找应用到了无序数组中。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 最优解:快慢指针法
这道题的最优解是使用快慢指针,时间复杂度O(n),空间复杂度O(1)。这个解法将数组视为链表,利用Floyd判圈算法来寻找环的入口。
2.1 算法原理
将数组索引和值看作链表节点和指针。因为有重复数字,所以必然存在环。例如数组[1,3,4,2,2]可以表示为:
0 → 1 → 3 → 2 → 4 → 2 → 4 → ... (形成环)
2.2 实现步骤
- 第一阶段:使用快慢指针找到相遇点
- 第二阶段:从起点和相遇点同时出发,再次相遇点即为环入口(重复数字)
python复制def findDuplicate(nums):
slow = fast = nums[0]
while True:
slow = nums[slow]
fast = nums[nums[fast]]
if slow == fast:
break
slow = nums[0]
while slow != fast:
slow = nums[slow]
fast = nums[fast]
return slow
2.3 复杂度分析
- 时间复杂度:O(n),最多遍历数组两次
- 空间复杂度:O(1),只使用了常数个额外变量
3. 边界条件与测试用例
3.1 常见测试用例
python复制测试用例1: [1,3,4,2,2] → 2
测试用例2: [3,1,3,4,2] → 3
测试用例3: [1,1] → 1
测试用例4: [1,1,2] → 1
3.2 特殊边界情况
- 最小数组:[1,1](n=1)
- 最大重复次数:[2,2,2,2,2](n=4)
- 重复数字在开头或结尾:[3,1,2,3]或[1,2,3,3]
4. 算法比较与选择
| 方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| 暴力解法 | O(n²) | O(1) | 不推荐 |
| 哈希表 | O(n) | O(n) | 通用解法 |
| 二分查找 | O(nlogn) | O(1) | 数字范围已知 |
| 快慢指针 | O(n) | O(1) | 最优解 |
在实际面试中,建议按照以下顺序思考:
- 先提出哈希表解法
- 优化空间复杂度提出二分查找
- 最后给出最优的快慢指针解法
5. 常见错误与调试技巧
5.1 典型错误
- 忽略题目条件:数字范围是[1,n],不是从0开始
- 快慢指针初始值设置错误:应该从nums[0]开始
- 二分查找时边界更新条件写反
5.2 调试建议
- 对于快慢指针法,可以打印每次迭代的slow和fast值
- 对于二分查找法,打印每次的mid和count值
- 使用小数组手动模拟算法执行过程
6. 相关题目拓展
这道题与以下几个LeetCode题目有相似之处:
-
- 环形链表 II(同样使用快慢指针找环入口)
-
- 丢失的数字(相反问题,找缺失的数字)
-
- 缺失的第一个正数(处理数组中的数字范围)
掌握这道题的核心在于理解如何将数组索引和值的关系转化为链表问题,以及如何应用Floyd判圈算法。这种思维方式在解决其他数组和链表问题时也非常有用。
我在实际刷题中发现,这类问题在面试中出现频率较高,特别是快慢指针的解法,既能考察算法思维,又能检验对基础数据结构的理解深度。建议在理解原理后,自己手动模拟几次算法执行过程,这样记忆会更加深刻。
