leetcode 287 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:

  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.

题意:有n+1个数字,范围从1到n,其中有一个数字会重复多次,用低于O(n2)的时间复杂度算法找出重复的数字,空间复杂的为O(1)。

解法:没有这些条件限制,就可以用二重循环直接找出来,或者用set/map等容器来做,正是由于这些条件限制,这题才变得有意思起来。

很不幸我没有独立解决出这道题,看题解都看了半天才明白,所以一定要用烂笔头记下来。

解法一:思路是采用了二分法+抽屉远离。首先解释一下为什么用二分法,因为O(n2)时间复杂度不能A,所以往下应该是n*logn,很容易联想到二分法,因为其复杂度为logn。

抽屉原理是说假设你有11个苹果,要放进10个抽屉,那么至少有一个抽屉里是有两个苹果的。

对应到这题,1~n的n+1个数字,有1个数字会至少重复两次。

比如取数组为{1,2,2,3,4,5},一共6个数,范围是1~5,其中位数应该是(5+1)/2 = 3,那么,如果小于等于3的数的个数如果超过了3,那么重复的数字一定出现在[1,3]之间,否则出现在[4,5]之间。以该数组为例,中位数为3,小于等于3的数一共有4个,大于3的数有两个,所以重复的数字在[1,3]之间。

代码:

int findDuplicate(vector<int>& nums)
{  
    int low = 1, high = nums.size()-1;    //low和high为数字的取值范围
while(low<high) { int cnt = 0; //cnt为不大于中位数的数字个数 int mid = (low + high)/2; for(int i=0;i<nums.size();i++) { if(nums[i] <= mid) cnt++; } if(cnt>mid) { high = mid; //如果不大于mid的数字个数比mid多的话,则重复数字应该出现在[low, mid]之间 } else low = mid+1; //如果不大于mid的数字个数比mid少的话,说明重复的数字出现在后半段中[mid+1,high] } return low; }

解法二:

posted @ 2016-03-13 13:21  puyangsky  阅读(1402)  评论(2编辑  收藏  举报