LeetCode——287寻找重复数 Floyd判圈

给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。

示例 1:

输入: [1,3,4,2,2]
输出: 2

示例 2:

输入: [3,1,3,4,2]
输出: 3

说明:

不能更改原数组(假设数组是只读的)。
只能使用额外的 O(1) 的空间。
时间复杂度小于 O(n2) 。
数组中只有一个重复的数字,但它可能不止重复出现一次。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-the-duplicate-number

题解

首先对nums数组进行建图,每个边都是inums[i]i\rightarrow nums[i]。由于存在重复的数字,因此这个位置的数字一定存在最少两条边指向它。因此整个图一定存在一个环。而我们要找的这个重复的数字就是环的入口。

那么如果找出环的入口呢。首先我们设置两个指针,一个慢指针slow表示每一次只走一步,另一个指针是fast表示每一次都走两步。根据Floyd判圈算法。两个指针在有环的情况下一定会相遇。

在这里插入图片描述
我们假设环的长度为LL,从起点到入口的步数为aa,从环的入口继续走bb步到达快慢指针相遇的位置。然后从相遇的位置继续走cc步回到环的入口,那么b+c=Lb+c=L,显然a,b,ca,b,c都是正整数。根据上述的定义,慢指针走了a+ba+b步,而快指针走了2(a+b)2(a+b)步。从另一个角度考虑,在相遇的位置,快指针比慢指针走了若干圈环。因此快指针也可以表示为a+b+kLa+b+kL,其kk表示快指针在环上走的圈数。因此
2(a+b)=a+b+kL2(a+b) = a+b+kL
所以a=kLba = kL-b
整理可得:
a=(k1)L+(Lb)=(k1)L+ca = (k-1)L+(L-b) = (k-1)L+c
如果快慢指针的步数都是一样的,慢指针从起点出发,走了aa步到达了环的入口,而快指针在相遇的位置出发,走了(k1)L+c(k-1)L+c步,因为相遇的位置,相当于从入口走了bb步,所以快指针也到达了环的入口。此时两个指针相遇。而这个相遇点就是环的入口。就是重复的数字。
因此这个题解答分为两个部分,一个是快慢指针步数不一致的时候的相遇点,作为快指针的起点。然后慢指针回到原点,快慢步数一致,然后在检测相遇点,就是答案。

func findDuplicate(nums []int) int {
    slow, fast := 0, 0
    for slow, fast = nums[slow], nums[nums[fast]]; slow != fast; slow, fast = nums[slow], nums[nums[fast]] { }
    slow = 0
    for slow != fast {
        slow = nums[slow]
        fast = nums[fast]
    }
    return slow
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章