(no.23) Find the Duplicate Number 多种方法探讨

来源:互联网 发布:ipad有淘宝卖家版吗 编辑:程序博客网 时间:2024/06/05 06:23
  • 题目描述
    这里写图片描述

  • 分析实现 法一
    不考虑空间复杂度必须为O(1)的条件,在满足1、3、4的条件下,对于找寻重复的数字来说,很简便的一种方法就是映射。将元素值作为新开数组的下标,进行出现次数的统计。

class Solution {public:    int findDuplicate(vector<int>& nums) {        int len=nums.size()+1;        vector<int> log(len,0);        int result=0;        for(int i=0;i<len;i++){            log[nums[i]]++;        }        for(int i=0;i<len;i++){            if(log[i]>1){                result=i;                break;            }        }        return result;    }};
  • 分析实现 法二
    现在,我们需要考虑空间复杂度必须为O(1)的条件,也即需要写出一个满足上述四个条件的代码,怎么入手呢?

    回顾法一,其成功在于以重复数字作为下标的元素被累加了两次及其以上,我们找到元素值大于1的,也就找到了重复数字。可是现在,不能创建个以N为大小的数组呀,我们怎样才能抓住下标元素被累加到了两次及其以上呢?

    因为有重复的数字,那么对于value初始化为0,value = nums[value]来说,value会最终陷在一个循环里。如nums={1,3,4,2,2},那么value=1,3,2,4,2,4,2,4,…..。而这个循环里,就有我们的重复数字。

    那现在问题就变为如何发现这个循环,并且如何从循环里拿到重复数字。所以便有了下面的代码:

class Solution {public:    int findDuplicate(vector<int>& nums){        if (nums.size() > 1){            //开始起跑,slow一步一步跑,fast两步并一步跑            int slow = nums[0];            int fast = nums[nums[0]];            //确定它俩陷在了循环里            while (slow != fast){                slow = nums[slow];                fast = nums[nums[fast]];            }            //fast重新开始一步一步地起跑,当与陷入循环里的slow相遇时,即为所求            fast = 0;            while (fast != slow){                fast = nums[fast];                slow = nums[slow];            }            return slow;        }        return -1;    }   };
  • 法2 细节说明
    这里我们引入另一个加速的循环,value = nums[nums[value]],如此,通过两个循环的碰撞便能发现了循环并拿到重复的元素。

    那为什么这两个循环一定会相撞呢?我们用物理学里的速度路程(s=vt)来论证。

    我们把慢的循环叫slow,快的循环叫fast。因为是循环永无止境,所以追踪的路程s可以无限大。

    现在,想让fast追上slow,fast的速度为vf,slow的速度为vs(vs结合本题的意思就是value = nums[value]计算一次),并且vf=2*vs。我们可以列出公式,vs*t=vf*t+dis。其中dis为一开始slow和fast相隔的距离。

    从而,我们可以解出t=dis/vs。
    现来看dis的取值,slow和fast最多相距n的距离。

    综上,它们一定会相撞。因为从物理学中知道,路程无限的条件下,快速度的一定会追上慢速度的。并且只要在O(n)的时间复杂度下,我们就能让两个循环相撞了。

  • 分析实现 法3
    之所以会想到法2,是受法1映射的思想影响。现在,我们从中跳出来,看看还有没有其他的办法。

    分治法。

    我们要充分利用题目给的条件,“Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive)”

    n+1个数,每个数的值还只能是从1到n中取,那么我随便说一个1到n中的数,如果数组中小于等于它的数的个数竟然大于该数值本身,那么肯定是有重复的。

    举个例子,nums={4,3,6,5,4,1,2},我随便说个数:5,那么nums中小于等于5的元素个数为6,说明Duplicate Number的值比5小,就是因为Duplicate Number,才会造成数量反超的局面。

    代码实现如下,使用二分搜索连缩小范围,

    class Solution {public:int findDuplicate(vector<int>& nums){       int len=nums.size();    int low=1;    int high=len-1;    int mid,count;    while(low<high){        mid=low+(high-low)/2;        count=0;        for(int i=0;i<len;i++){            if(nums[i]<=mid)   //统计小于mid的元素个数                 count++;        }        if(count<=mid)            low=mid+1;        else         //说明有元素重复了不止一次             high=mid;       }    return low;}};
原创粉丝点击