LeetCode-287: Find the Duplicate Number(寻找重复数字——弗洛伊德循环寻找算法)

Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.

Example 1:

Input: [1,3,4,2,2]
Output: 2

Example 2:

Input: [3,1,3,4,2]
Output: 3

Note:

  1. You must not modify the array (assume the array is read only).
  2. You must use only constant, O(1) extra space.
  3. Your runtime complexity should be less than O(n2).
  4. There is only one duplicate number in the array, but it could be repeated more than once.

思路

包含有n+1个不大于n的正整数的数组一定存在重复元素,本题要求从这样的数组中找出重复的那个元素。题目很简单,有很多方法可做,但是难在要求空间复杂度O(1),时间复杂度小于O(n2),并且源数组是只读的,不允许任何修改操作。这样就限制了诸如Hash表,排序等的解法。最优解法是采用弗洛伊德循环寻找算法,原理如下:

  1. 需要明确一点:n+1个不大于n的正整数组成的数组可看做一个链表:
    • A[0]=1 >>>> A[1]=3 >>>> A[3]=2 >>>> A[2]=4 >>>> A[4]=2
i 0 1 2 3 4
A[i] 1 3 4 2 2

如果这样的数组中出现重复元素,那么这个链表就会出现一个环

  • A[0]=1 >>>> A[1]=3 >>>> A[3]=2 >>>> A[2]=4 >>>> A[4]=2
  1. 弗洛伊德循环寻找算法就是查找这个环入口的算法,过程为:
    (1)算法采用了一个慢指针和一个快指针,慢指针每次移动1步,快指针每次移动2步,初始两个指针都指向0,由于路径中存在环,两个指针最终肯定会汇合。第一次汇合时,我们将快指针重置为0,并将移动步数从2修改为1,等到两个指针再次汇合时,指向的元素即为环入口。
    (2)原理推导:如下图,P段表示链表中的非环部分,C表示慢指针与快指针第一次汇合时在环中重合的部分,L表是整个环的长度,X表示L-C部分;
    LeetCode-287: Find the Duplicate Number(寻找重复数字——弗洛伊德循环寻找算法)_第1张图片
    (3)则当快指针与慢指针第一次汇合时,慢指针走过的路径长度为P+C,快指针走过的路径长度为L+C+nL(n为整数,由于快指针速度快,因此快指针一定是比慢指针多跑了n圈);然后由于快指针的速度是慢指针速度的2倍,而他们经过的时间是相同的,因此路程有如下关系:
    P + C + n L = 2 ( P + C ) = > P = ( n − 1 ) ∗ L + L − C = ( n − 1 ) L + X P + C + nL = 2 (P + C) => P = (n - 1) * L + L - C = (n - 1) L + X P+C+nL=2(P+C)=>P=(n1)L+LC=(n1)L+X
    因此可得出结论:“P段的长度”等于“X段的长度+整数圈环的长度”,故此时只需要让快指针重置为0(即回到P段的开始)并使速度与慢指针相同,则他们一定会在环路入口的位置再次汇合。

Java实现

class Solution {
    public int findDuplicate(int[] nums) {
        int slow = 0;
        int fast = 0;
        do {
            slow = nums[slow];
            fast = nums[nums[fast]];
        }while (slow != fast);

        fast = 0;

        while (slow != fast) {
            slow = nums[slow];
            fast = nums[fast];
        }
        return slow;
    }
}

Python实现

class Solution(object):
    def findDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        slow = 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 fast

Scala实现

object Solution {
    def findDuplicate(nums: Array[Int]): Int = {
        var slow = 0
        var fast = 0
        do {
            slow = nums(slow)
            fast = nums(nums(fast))
        } while (slow != fast)

        fast = 0

        while (slow != fast) {
            slow = nums(slow)
            fast = nums(fast)
        }

        return fast
    }
}

你可能感兴趣的:(LeetCode)