这串数字里,哪两个指针会相遇?
摘要:本题在线练习:LeetCode 287. 寻找重复数 - 在线练习(免费 · 无需登录 · AI 辅助)(https:onefly.topzero2Leetcodeplaygrou
本题在线练习:LeetCode 287. 寻找重复数 - 在线练习(免费 · 无需登录 · AI 辅助)(https://onefly.top/zero2Leetcode/playground.html?id=287)
配套刷题网站 Zero2Leetcode —— 内置本地 OJ + AI 教练,零门槛开始刷 Hot 100。
题目概述
给定一个长度为 n+1 的数组 nums,数字范围是 [1, n],保证至少存在一个重复数字,且只要找出任意一个重复数字即可。
要求:
不能修改数组(在原题约束下)
额外空间尽量小
时间尽量快
这题的最经典解法是 Floyd 判环(快慢指针),把数组当成“链表”来理解。
核心思路:nums 定义了一个从下标到下标的映射
因为 nums[i] 的取值在 [1, n],可以把它当成“下一跳的下标”:
i -> nums[i]
从下标 0 出发不断跳转:
0 -> nums[0] -> nums[nums[0]] -> ...
由于可到达的节点只有 1..n 这 n 个,但路径长度是 n+1 的跳转过程,必然会出现重复访问,也就是形成环。
环的入口对应的值,就是重复数字。
代码实现(Floyd 判环)
class Solution:
def findDuplicate(self, nums: list[int]) -> int:
# phase 1: find intersection in the cycle
slow = nums[0]
fast = nums[nums[0]]
while slow != fast:
slow = nums[slow]
fast = nums[nums[fast]]
# phase 2: find entrance to the cycle
slow = 0
while slow != fast:
slow = nums[slow]
fast = nums[fast]
return slow
逐行拆解:为什么第二阶段能找到入口
Floyd 判环的结论:当快慢指针在环内相遇后,把其中一个指针放回起点,两个指针都每次走一步,它们会在环的入口相遇。
这里的“起点”用下标 0 表示,“走一步”就是 x = nums[x]。
环入口对应的值(也就是下标)就是重复数字。
手动模拟(直觉版)
例如 nums = [1,3,4,2,2]:
映射:
0 -> 1
1 -> 3
3 -> 2
2 -> 4
4 -> 2(这里回到 2,形成环 2 -> 4 -> 2)
入口是 2,所以重复数是 2。
进阶补充:二分计数法(可选思路)
也可以对值域 [1, n] 二分,统计 <= mid 的元素个数:
如果 count > mid,重复数在 [1, mid]
否则在 [mid+1, n]
时间 O(n log n),空间 O(1),但 Floyd 是 O(n) 更好。
复杂度分析
Floyd 判环:
时间:O(n)
空间:O(1)
总结
这题最关键的转化是:
把 nums[i] 当作下一跳下标,数组就变成了一个带环的“指针图/链表”。
理解了这个转化,Floyd 判环就顺理成章,且满足不修改数组、常数空间的要求。
