给定一个长度为 n+1 的数组nums,数组中所有的数均在 1∼n 的范围内,其中 n≥1。
请找出数组中任意一个重复的数,但不能修改输入的数组。
样例
给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。
返回 2 或 3。
思考题:如果只能使用 O(1) 的额外空间,该怎么做呢?
思路:
不能修改数组,原来的方法,移动元素让下标和值相对应这个方法就用不了,又不能用额外的空间,也就是不能用打卡标记的方法了。正确的姿势是分治法,分的区间是数的范围,*如果在这个范围的数字个数大于区间长度,那么这个区间内一定有数字重复了。*继续对该区间进行划分,直到区间长度为1为止。时间复杂度:分治法为O(lgn),每次都要统计区间范围内的数字,复杂度为O(n)
所以总的复杂度为O(nlgn)。
1、如样例nums = [2, 3, 5, 4, 3, 2, 6, 7],分区间1-3,4-7;前面的区间范围内的数字个数为4,后面为3,很明显前面的区间内有数字重复了。
2、继续对前面的区间划分:1-2,3;前面的区间范围内的数字个数为2,后面为2,后面的区间有数字重复了(题目只让我们返回一个重复的数字,如果有多个随便挑一个就OK了)。
3、区间长度为1,个数却有两个,这个数字重复了,退出循环,返回该数字。
参考代码:
class Solution {
public int duplicateInArray(int[] nums)
{
int l=1,r=nums.length-1;
int mid=(l+r)/2;
while(l!=r)
{
int lNum=cnt(nums,l,mid);
int rNum=cnt(nums,mid+1,r);
if(lNum>mid-l+1)
r=mid;
else
l=mid+1;
mid=(l+r)/2;
}
return l;
}
private int cnt(int[] nums, int l, int mid)
{
int count=0;
for(int in:nums)
if(in>=l&&in<=mid)
count++;
return count;
}
}