[Leetcode]287. Find the Duplicate Number

这是Leetcode第287题,给定一个包含n + 1个整数的数组,其中每一个整数均介于[1, n]之间,证明其中至少有一个重复元素存在。假设只有一个数字出现重复,找出这个重复的数字。
这道题可以用计数排序的思路求解,但更为巧妙的方法是使用快慢指针求解。
基本思想是将数组抽象为一条线和一个圆环,因为1~n 之间有n+1个数,所以一定有重复数字出现,所以重复的数字即是圆环与线的交汇点。然后设置两个指针,一个快指针一次走两步,一个慢指针一次走一步。当两个指针第一次相遇时,令快指针回到原点(0)且也变成一次走一步,慢指针则继续前进,再次回合时即是线与圆环的交汇点。

快慢指针

明白了上面的原型后,我们来看这个变型。把数组抽象成线和圆环,举例来说,假设我们有一个数组是nums[]=[1,2,3,4,5,5,6,7],pf代表快指针,ps代表慢指针,初始ps指向nums[0],即1,pf指向nums[nums[0]],即2,行动一次后,ps指向nums[1],即2,pf指向nums[nums[2]],即4,再动一次,ps指向nums[2],即3,pf则指向了nums[nums[4]],即5;可以发现pf一旦指向5后便不会再动,因为nums[5]一直为5,直到ps慢慢追上,然后令pf从头开始,ps一直在5处停留,最后定会相遇在这里,而这里就是重复数字。这里举了个最简单的例子,是为了方便大家理解,实际上实际的圆环顺序与数组的顺序是没有关系的,不信可以自己在纸上画一画,当数组变成nums[]=[4,6,5,1,3,2,5,7]的样子,你会更加理解这个算法的!

具体代码如下:

class Solution:
    def findDuplicate(self, nums: List[int]) -> int:
        for i in range(len(nums)):
            while nums[i] != i+1:
                index= nums[i]-1
                if nums[i] == nums[index]:
                    return nums[i]
                else:
                    nums[i],nums[index] = nums[index],nums[i]

快慢指针解法具体代码如下:

class Solution:
    def findDuplicate(self, nums: List[int]) -> int:
        slow = 0
        fast = 0
        while True:
            slow = nums[slow]
            fast = nums[nums[fast]]
            if slow == fast:
                 break
        fast = 0
        while slow!=fast:
            slow   = nums[slow]
            fast = nums[fast]
    
        return slow

快慢指针的题也很常见了,在求环问题中更是经典的解法。用数组索引来代表环的位置,也是一个很trick的思想。

posted @ 2019-10-04 16:11  Jamest  阅读(199)  评论(0编辑  收藏  举报