Find the Duplicate Number (寻找重复数字)

Posted LeaveMeAlone1995

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Find the Duplicate Number (寻找重复数字)相关的知识,希望对你有一定的参考价值。

对于一个长度为n+1的数组,其中每一个值的取值范围是[1,n],可以证明的是必然存在一个重复数字(抽屉原理),假设仅存在一个重复数字,找到他。

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

自己做的时候,要么时间复杂度到o(n2),要么需要额外的存储空间利用hashset,下面来分析一下leetcode上别人的算法吧。

方法一:通过图论中环的有关知识解决

public int findDuplicate(int[] nums) {
        // Find the intersection point of the two runners.
        int tortoise = nums[0];
        int hare = nums[0];
        do {
            tortoise = nums[tortoise];
            hare = nums[nums[hare]];
        } while (tortoise != hare);

        // Find the "entrance" to the cycle.
        int ptr1 = nums[0];
        int ptr2 = tortoise;
        while (ptr1 != ptr2) {
            ptr1 = nums[ptr1];
            ptr2 = nums[ptr2];
        }

        return ptr1;
    }

如果数组的每一个数的取值都是不重复的,那么可以选取特定的数值来使,不断通过索引值得到数值,再将新的数值作为索引值,循环下去可以得到一个链路。假定数组为[1,2,3,4,5],设定f(x),x是索引值,f(x)是值,并将得到的f(x)作为下一个输入,这样形成了一个链路,1->2->3->4->5;但是如果稍微做一下改变原数组为[1,2,3,4,1],那么链路将变为,1->2->3->4->1…,形成一个环路,这里的重复数字1就是构成环路的关键。

本题中就包含这样一个重复数字,所以数组nums一定会存在一个环路,问题变为如何查找环路起点问题,对于这种问题有这样一个算法,叫做弗洛伊德的循环寻找算法。在算法中会有两个指针一个快速指针每次移动两个步骤,一个慢速指针每次移动一个步骤,其中快速指针会提前进入循环并且在慢速指针进入循环后会与其相交。类似于操场跑圈,快速指针和慢速指针同时同宿舍出发,快速指针先到操场开始跑圈,慢速指针后到操场开始跑圈,但是快速指针一定会在某个时刻与慢速指针到达同一位置。

如何求取圆环起点位置(即重复的数字):

设定慢速指针与快速指针相交点距离环起点的距离为k,环周长为n,指针起点到环起点的距离为m,则慢速指针走过的距离为a = m+k+xn ;快速指针走过的距离为2a = m+k+yn,两者做差可以得到a = (y-x)n,是环长度的整数倍,如果将快速指针重置到起点,且将快速指针的移动距离改为1,那么当快速指针移动到圆环起点时,慢速指针移动距离为a+m,因为a是圆环长度的整数倍,所以慢速指针的位置也是在圆环起点,这样两者的相遇点即为圆环起点。

方法二:

    public int findDuplicate_leetcode3(int[] nums) {
        if (nums.length == 0 || nums == null)
            return 0;
        int low = 1, high = nums.length - 1, mid;
        while (low < high) {  //这个题不好用low+1<high,因为最后结果的两个值无从比较,是基于count的
            mid = low + (high - low) / 2;
            int count = 0;
            for (int i = 0; i < nums.length; i++) {
                if (nums[i] <= mid)
                    count++;
            }
            if (count > mid)
                high = mid;
            else
                low = mid + 1;
        }
        return low;
    }

根据题目中的条件,可以知道的是一定会有重复数字且只有一个重复数字,那么必然导致数组数值的不均匀分布(指的不是位置的不均匀,而是数值的取值不均匀),那么可以通过二分法来判别重复取值的数字在哪个部分,取数组的中值,计算大于和小于中值的数量,取数量较多的那份为新的数组,重复进行,最后到不可分时,得到的即为重复数字。

以上是关于Find the Duplicate Number (寻找重复数字)的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode: Find the Duplicate Number

LeetCode 287. Find the Duplicate Number

287. Find the Duplicate Number *HARD*

287. Find the Duplicate Number

287. Find the Duplicate Number

287. Find the Duplicate Number