Find the Duplicate Number

来源:互联网 发布:大数据广告精准投放 编辑:程序博客网 时间:2024/05/16 07:09

问题描述:

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.

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.
本文将时间复杂度定为O(n)

        这个问题不是很难,但是如果将时间复杂度限制了,就对算法有很高的要求。

        首先,我们先抛开时间复杂度的要求,这样就会很容易的想到三个解法:

1、暴力破解O(n^2)这个不解释

        2、稍微复杂一点,首先利用快速排序,得到一个有序序列,然后就可以遍历出duplicate element了。复杂度O(nlogn)。

        3、利用二分查找的思想:

              由于数组中的元素只有一个是有重复的,那么对于任意一个元素k,如果比k小的元素的个数为k-1(1~k-1),那么在k之前一定没有重复的元素,则我们就向后查找,否则就

向前查找。这个的时间复杂度也是O(nlogn)。

   

看起来似乎不太可能有更简单的解法了,非也。这个解法可是困扰了大神唐纳德·克努特24小时,所以咱常人想不到也是正常的。


下面直入主题吧,我们怎么做到时间复杂度O(n),空间复杂度O(1),的呢?

     

         我们首先建立一下模型:

         1、链表模型,

               我们把数组A = {1,2,3...,n},建立一个映射,并且射到它自己。f(i) = A[i]-1,下面问题就变为了在i != j 的情况下,找到一对(i,j)使得f(i)==f(j)。下图为对链表建立的模型。

               x_0  = n; 

              x_ 1 =  f(x_0);

              x_2 =  f(x_1);

              ...........

              x_k = f(x_k-1);




         我们可以看到当转化成为链表模型之后我们只需要找到链表的入口,就可以找到重复的元素了。

          但是,这只是一个大体的思路,我在做的时候有过几个思考。

          1)对于A={1,2,3,2}来说,我们在按照链表索引的时候f(2) = A[2] - 1 = 3 - 1 = 2; 然后 就在这里不断循环,因为它是指向它自己的。那么此时算法会不会失效?

               答案是:不会。因为当指向自己的元素不是重复元素的时候,也就是说它只会出现一次,那么不会有元素指向它(因为它已经指向了自己),所以它不会出现在链表

中,如果它是重复的元素,那么也不会对算法产生影响,因为此时只能访问到重复的元素。

          2)为什么要从n开始?

                因为我们元素的范围是1~n,f(i)= A[i] - 1;也就是说如果从前面开始无法访问到位置为n的元素。


        2、找到链表入口

              此时我们可以仅仅在链表模型上面考虑如何找到链表入口。

              我们从链表头开始,假设从链表头到链表入口的距离为C,链表长度为L。有两个指针遍历链表,一个是快指针,一个是慢指针,慢指针一次移动一个节点,快指针的速度是慢指针的两倍。

             考虑当两个指针相遇时。第一次相遇的点一定是mL,且(m-1)L < C , mL >= C。 因为当前进的不够C的时候,还没有进入环,不会相遇,进入环以后,慢指针的位置为p,快指针前进了2mL,在mL的位置上又绕了环走了m圈,则位置还是在p上。


             现在快指针不动了,另外一个慢指针从头开始走,p位置上的慢指针随着它一起动,两个指针就会在环的入口处相遇。

 

             因为在入口处,新指针前进了C = mL - p ,而慢指针前进了相同的距离。所以慢指针的位置是mL - p + p = mL 在环的起点处。


                          现在我们找到了入口节点,也就是说找到了一对(i,j) i != j。有f(i) ==f(j)。即找到了这个重复的元素,时间复杂度也为O(n)。


   参考文献:http://keithschwarz.com/interesting/code/?dir=find-duplicate

        

      

            

0 0
原创粉丝点击