题目链接:287. Find the Duplicate Number
题目描述:
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,找出 这个重复的数 。
设计的解决方案必须不修改数组 nums 且只用常量级 O(1) 的额外空间。
分析:
寻找重复数的常用思路:
1)排序+遍历
排序可以使用归并排序、堆排序、快速排序等时间复杂度为O(n logn)的算法,然后再遍历找出重复数。需要修改数组nums。
2)哈希表
可以使用set 或map,也可以自己构建哈希表。如果不修改nums,那么构建哈希表的空间复杂度就是O(n),如果修改nums,那么构建哈希表的空间复杂度就是O(1)。时间复杂度都是O(n)。
3)计算
如果一共有n+1个元素,其取值都在1~n之间,并且其中只有一个元素出现了2次,其他元素都出现了且只出现了一次。那就可以用n+1个元素的和减去1~n这n个元素的累加和,结果就是那个重复出现的元素。时间O(n),空间O(1)。没有修改nums。
但是本题要求不修改nums,额外空间是O(1),而且虽然只有一个重复出现的元素,但是该元素出现了2次及以上。所以上述三种方法都不适用。
经过查看题解分析思路如下:
1)二分法
cnt[i]表示nums中数据小于等于i的个数。i∈[0,n]。
如果共有n个元素,取值范围为1~n,且不存在重复元素,即每个元素都出现且只出现一次,那么得到cnt[i]==i。
如果共有n+1个元素,取值范围为1~n,只有一个元素出现2次,其他元素都出现且只出现1次。如果出现2次的元素为target,那么对于i∈[1,target-1],cnt[i]==i,对于i∈[target,n],cnt[i]==i+1.
如果共有n+1个元素,取值范围是1~n,只有一个元素重复出现,且出现的次数多于2次,那么一定有一些元素没有出现过。假设某个没有出现过的元素为x。若x<target,那么对于i∈[1,x-1],cnt[i]==i,对于i∈[x,target-1],cnt[i]<i;若x>target,那么对于i∈[target,n],cnt[i]>i。
综上:对于i∈[1,target-1],cnt[i]<=i,对于i∈[target,n],cnt[i]>i。即cnt[i]是随着i递增的。
所以目标就是在1~n之间找到一个数target,使nums中<=target的数的个数大于target。且这样的target尽可能小。
因为i和cnt[i]都是有序的,所以可以用二分法进行查找。其实也不必先把cnt[i]都求出来,然后二分去判断cnt[i]和i的大小。这样的时间复杂度是O(n^2),空间O(n)。
可以二分遍历i,对于i再确定对应的cnt,不满足条件再更换区间。这样需要的时间复杂度是O(n logn),空间是O(1)。
js代码如下:
var findDuplicate = function(nums){
let length=nums.length;
let l=1,r=length-1;
let mid,cnt=0,res;
while(l<=r){
mid=l+((r-l)>>1);
cnt=0;
for(let i=0;i<length;i++){
if(nums[i]<=mid){
cnt++;
}
}
if(cnt<=mid)
l=mid+1;
else{
r=mid-1;
res=mid;
}
}
return res;
}
2)快慢指针
由于一共有n+1个元素,取值都在1~n之间,如果创建一个由i指向nums[i]的链表,一定可以成环形链表。那么重复出现的元素就是环形链表入环处的元素。
nums=[1,3,4,2,2]
链表为:1->3->2->4->2->4->2->....,可见,从2开始入环。
求环形链表入口元素的分析,可以参考:环形链表II
代码如下:时间O(n),空间O(1)。
var findDuplicate = function(nums) {
let slow=0;
let fast=0;
while(true){
slow=nums[slow];
fast=nums[fast];
fast=nums[fast];
if(slow===fast)
break;
}
fast=0;
while(fast!==slow){
fast=nums[fast];
slow=nums[slow];
}
return slow;
};

这篇博客探讨了在一个1到n的整数数组中,如何在不修改数组且仅使用常量级额外空间的情况下找出重复的数。分析了排序、哈希表和计算等传统方法的局限性,并详细介绍了利用二分法和快慢指针两种O(nlogn)时间复杂度的解决方案。二分法通过判断小于等于目标数的元素个数来找到重复数,而快慢指针则构建了一个环形链表的概念,找到环的入口即为重复数。

799

被折叠的 条评论
为什么被折叠?



