【LeetCode-查找】寻找重复数
题目描述
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例:
输入: [1,3,4,2,2]
输出: 2
输入: [3,1,3,4,2]
输出: 3
说明:
- 不能更改原数组(假设数组是只读的)。
- 只能使用额外的 O(1) 的空间。
- 时间复杂度小于 O(n2) 。
- 数组中只有一个重复的数字,但它可能不止重复出现一次。
题目描述: https://leetcode-cn.com/problems/find-the-duplicate-number/
思路
题目中的要求:
- 不能更改原数组(假设数组是只读的),说明不能用排序的方法;
- 只能使用额外的 O(1) 的空间, 说明不能使用哈希表;
- 时间复杂度小于 O(n2),说明不能用两层循环;
可以使用二分查找的思想来做。首先介绍一下鸽巢原理:n+1 只鸽子放到 n 个鸽巢里,则肯定有一个鸽巢中的鸽子个数大于 1。则我们设置两个指针 left 和 right,计算 mid = (left+right)/2。统计输入数组 nums 中小于等于 mid 的元素个数 cnt,如果 cnt>mid,则说明 [left, mid] 之间的元素个数超过了 mid,则重复的数字一定在 [left, mid] 之间,否则重复的数字在 [mid+1, right] 之间。循环结束时,left 就是重复的元素。代码如下:
写法1:
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int left = 1;
int right = nums.size()-1;
while(left<right){
int mid = left+(right-left)/2;
int cnt = 0;
for(int num:nums){
if(num<=mid) cnt++;
}
if(cnt>mid){
right = mid; // 因为是循环条件是left<right,所以这里是 right=mid;
}else{
left = mid+1;
}
}
return left;
}
};
写法2:
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int left = 1;
int right = nums.size()-1;
while(left<=right){
int mid = left+(right-left)/2;
int cnt = 0;
for(int num:nums){
if(num<=mid) cnt++;
}
if(cnt>mid){
right = mid-1;
}else{
left = mid+1;
}
}
return left;
}
};
写法 1 和写法 2 的区别在于更改了循环条件和 right 的更新方式,这一点和二分查找是一样的。
- 时间复杂度:O(nlogn)
- 空间复杂度:O(1)