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
Note:
- You must not modify the array (assume the array is read only).
- You must use only constant, O(1) extra space.
- Your runtime complexity should be less than
O(n2)
. - There is only one duplicate number in the array, but it could be repeated more than once.
对于一维数组,其中的数字有这样1-n的规定的时候,首先会想到一定要用到数组下标索引和数组中内容的相关性。但是,此题目中的难点在于,不能改变数组内容,也不能额外申请大于常数级的空间。但是,充分应用题目中的条件一定是没错的。在oj上看了相关思想后,总结为一下两种方法:
解法一:时间:O(nlogn)
首先想到最后的值一定是一个1-n之间的数,他重复了。那么久在1-n之间通过二分查找法的方法查找。这个查找在于,首先左右以1和n为端点,取中间的(1+n)/2为索引mid,然后计数数组中小于等于mid的数有多少个,如果小于等于mid的数字超过了mid,那么重复的数一定在1-mid之间,否则则在mid+1到右端点n之间,如此循环下去,直至左端点不小于右端点。C++代码如下
int findDuplicate(vector<int>& nums) { if (nums.size()==0) return -1; int left=1, right=nums.size()-1; while(left<right) { int mid=(left+right)/2; int count=0; for(int temp:nums) { if (temp<=mid) ++count; } if (count<=mid) left = mid+1; else right = mid; } return left; }
解法二:时间O(n)
说一下,本文怎么连成链表的:比如数组:2,1,3,5,4,7,6,3,8
链表:2->3->5->7->3->5->……是不是就练成环了,按照数组中下标对应的值为下标一直连下去,而在程序中,只是替换数组的下标而已。
把这个数组通过下标索引的方式连接成一个链表,因为有一个数字重复了,所以是一个有环的链表。想象一下,非环的部分的最后一个元素指向下一个环的入口,环中最后的一个元素也指向环的入口,是的,这两个数字是一样的,就是它重复了,才有了这个环,我们要找的就是这个数字。即环之间的一个数或者说环中最后一个元素。
那么这个问题是不是就换算成求有环链表中找到环的入口节点的问题了(本题中找到入口的上一个位置,本文使用数值值不用指针地址,因为这里入口上一个元素与环的最后一个元素就已经相等了)
代码:
int findDuplicate(vector<int>& nums) { if (nums.size()==0) return -1; int slow=nums[0], fast=nums[nums[0]]; while(slow!=fast) { slow = nums[slow]; fast = nums[nums[fast]]; } fast = 0; while (fast != slow) { fast = nums[fast]; slow=nums[slow]; } return fast; }
尤其是第二种方法,不得不让人佩服啊