287. Find the Duplicate Number

方法一:应用鸽笼原理进行二分法查找

因为在1~n的范围内最多只能放n个不同的数字,但现在数组放了n+1个数字,所以至少有一个重复。

对于在1~n范围内的某个数字m,那么数组中小于等于m的数字最少有m个,并且刚好为m的时候,1~m之间不会有重复,比m大时,重复数字必在1~m中。

public class Solution {
    public int findDuplicate(int[] nums) {
        int low = 1, high = nums.length-1;
        while (low<high) {
            int mid = (low+high)/2;
            int count = 0;
            for(int num: nums) if (num<=mid) count++;
            if (count > mid) high = mid; else low = mid + 1;
        }
        return low;
    }
}

 方法二:映射找环法

假设数组中没有重复,那我们可以做到这么一点,就是将数组的下标和1到n每一个数一对一的映射起来。比如数组是213,则映射关系为0->2, 1->1, 2->3。假设这个一对一映射关系是一个函数f(n),其中n是下标,f(n)是映射到的数。如果我们从下标为0出发,根据这个函数计算出一个值,以这个值为新的下标,再用这个函数计算,以此类推,直到下标超界。实际上可以产生一个类似链表一样的序列。比如在这个例子中有两个下标的序列,0->2->3

但如果有重复的话,这中间就会产生多对一的映射,比如数组2131,则映射关系为0->2, {1,3}->1, 2->3。这样,我们推演的序列就一定会有环路了,这里下标的序列是0->2->3->1->1->1->1->...,而环的起点就是重复的数(Floyd's Algorithm)。

考虑如下的一个序列:

从idx=0开始,对应的val是下一个idx,建立一个类似链表的结构, 1->3->2->4->2 将会出现一个环,之后就是链表判环的解法,可以用快慢指针来做。环的入口就是重复的数字。 

                                                     

class Solution {
    public int findDuplicate(int[] nums) {
        int slow = nums[0], fast = nums[0];
        
        for(;;) {
            
            slow = nums[slow];
            fast = nums[nums[fast]];
            if(slow==fast)
                break;
        }

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值