今天是2020年5月26日,星期二。
题目描述
给定一个包含 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) 。
- 数组中只有一个重复的数字,但它可能不止重复出现一次。
题目分析
在本题目中,使用「二分查找」的方法来解决。使用「二分查找」的前提:长度为n+1的数组nums,数组中每个值均在1~n之间(包括1和n)。注意说明4中,说明了一种极端情况:即数组中所有元素为同一个值。
由于本题目的特殊性,可以使用「二分查找」来解决。由于所有元素值均在[1,n]之间。那么其中位数为mid=(1+n)/2
。我们遍历整个数组元素,拿每个元素的值与mid值相比,统计x<=mid
值的个数为cnt。若cnt > mid值,则说明在区间[1,mid]之间存在重复的值,即重复的值位于1~mid之间,此时移动right=mid
;反之则令left=mid+1
。
退出循环后必定有left == right
,当前下标即为重复的数据。
说明:由于这个算法是空间敏感的,「用时间换空间」是反常规做法,算法的运行效率肯定不会高。
参考代码
class Solution {
public int findDuplicate(int[] nums) {
int length = nums.length;
int right = length - 1;
int left = 1;
while (left < right) {
int mid = left + (right - left) / 2;
int count = 0;
for (int i = 0; i < length; i++) {
if (nums[i] <= mid) {
count++;
}
}
if (count > mid) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
}
时间复杂度:O(NlogN),二分查找的时间复杂度为O(logN),由于在二分查找中有一个for循环复杂度为O(N),所以整体时间复杂度为O(NlogN)。
空间复杂度:O(1),仅使用了一个count去记录个数。