难度:中等
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例 1:
输入: [1,3,4,2,2]
输出: 2
示例 2:
输入: [3,1,3,4,2]
输出: 3
说明:
不能更改原数组(假设数组是只读的)。
只能使用额外的 O(1) 的空间。
时间复杂度小于 O(n2) 。
数组中只有一个重复的数字,但它可能不止重复出现一次。
思路1:查找重复数字,首先想到的是通过哈希查找,存在在set里,查看是否再次出现,但这样会需要O(n)空间。题目中要求O(1)空间。故重新审题,可发现n+1个数,数字是n+1的空间,让数字都在应该出现的位置即可,不在自己位置的就是重复了。解法有两种,一是通过排序,然后遍历,如快排(nlogn复杂度),二是一次遍历,发现位置不对便将该数字放到应该在的地方,并将那个位置放到当前继续查找。
示例:1 3 4 2 2
方法2:第一步,位置0是1(数组从0开始),没有问题;
第二步,位置1是3,将3放回位置2,并将位置2的4放到位置1;
第三步,位置1是4,将4放到位置3,并将位置3的2放到位置1;
后面一直到位置4,数字是2,与位置1进行比较发现相等。即返回2.
class Solution {
public:
int findDuplicate(vector<int>& nums) {
if(nums.empty()) return 0;
int i=0;
while(i<nums.size())
{
if(nums[i]==i+1) i++;
else
{
if(nums[i]==nums[nums[i]-1]) return nums[i];
int tmp = nums[nums[i]-1];
nums[nums[i]-1] = nums[i];
nums[i] = tmp;
}
}
return 0;
}
};
思路2(5月27更):leetcode官方题解中方法1,二分查找
定义cnt[i]表示nums[]数组中小于等于i的数有多少个,如果说对于数字i,小于i的数里不存在重复数,则满足cnt<=i;否则表示存在重复数。以坐标i(数据一共有i个)为判断,检测在某个范围内是否存在重复数。范围的确定采用二分法,定义了left,right。从数据中心mid(坐标值)开始搜索,如果mid>cnt,则表示在mid左边范围内存在重复数,只在左半边搜索,否则另一边。
class Solution {
public:
int findDuplicate(vector<int>& nums) {
if(nums.empty()) return 0;
int n=nums.size();
int left=1,right=n-1,ans=0;
while(left<=right){
int mid=(left+right)>>1;//注意这里的left,right,mid均为坐标值。
int cnt=0;
for(int i=0;i<n;++i){
cnt += nums[i]<=mid;
}
if(cnt<=mid)
left=mid+1;
else{
right=mid-1;
ans=mid;
}
}
return ans;
}
};