LeetCode 287
最简单的想法就是先排序,然后遍历去检查。
def findDuplicate2(self, nums: List[int]) -> int:
nums.sort()
for x in range(1, len(nums)):
if nums[x] == nums[x-1]:
return nums[x]
但是题目里面给出了两个限制:
- You must not modify the array (assume the array is read only).
- You must use only constant, O(1) extra space.
前面的排序就破坏了第一个条件。需要考虑其他的方法,如果我们把数组中的值想象成link,那么其实可以把数组理解为一种点直接的链表连接,重复的时候其实就是进入了循环。按照例子【1,3,4, 2, 2】,把数字看出链表的指针,那么就是 1 --》3–》2–》4–》2. 所以这个问题可以转换为在链表中找loop的起始点的问题。
那么我们就可以使用 Floyd’s Tortoise and Hare 算法,
这个算法实现比较简单,原理需要一点证明:
假设Loop长度是L = y+z,
在相遇的点,我们知道 x + n1L + y 是乌龟跑过的距离
x+n2L+y 是兔子跑过的距离。又有兔子的速度是乌龟的两倍,那么
2x+2n1L+2y = x +n2L + y =>
x + 2n1L +y = n2L =>
x +2n1L + L = n2L + Z,
左右同时对L取mod,那么我们就有 x % L = z % L, 由于Z小于L,那么就有 z = x % L.
通过这个我们知道,如果从x点和meeting-point出发,最终他们会在loop起点的地方相遇。
def findDuplicate(self, nums: List[int]) -> int:
fast = nums[nums[0]]
slow = nums[0]
while slow != fast:
fast = nums[nums[fast]]
slow = nums[slow]
slow = 0
while slow != fast:
slow = nums[slow]
fast = nums[fast]
return fast
参考: https://cs.stackexchange.com/questions/10360/floyds-cycle-detection-algorithm-determining-the-starting-point-of-cycle