本题来自 LeetCode:287. 寻找重复数[1]
题目详情
给定一个包含
n + 1
个整数的数组
nums
,其数字都在
1
到
n
之间(包括
1
和
n
),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例 1:
输入: [1,3,4,2,2]
输出: 2
示例 2:
输入: [3,1,3,4,2]
输出: 3
说明:
题目分析
暴力法
若不考虑说明
3
的内容,可以直接用暴力法解答出来。暴力法只需要两个循环进行比较即可。
时间复杂度
O(n^2)
,空间复杂度
O(1)
。
class Solution {
public int findDuplicate(int[] nums) {
for(int i = 0; i nums.length; i ++) {
for(int j = i + 1; j nums.length; j ++) {
if(nums[i] == nums[j]) {
return nums[i];
}
}
}
throw new RuntimeException();
}
}
替换法(抽屉原理)
若不考虑说明
1
的内容,可以通过替换法解答。核心思想是将数值
i + 1
放置到数组
nums
的
i
位置上 。若该位置已放置正确的数值,则该数值就是重复的。
时间复杂度
O(n)
,空间复杂度
O(1)
。
class Solution {
public int findDuplicate(int[] nums) {
for(int i = 0; i nums.length; i ++) {
while(nums[i] != i + 1) {
if(nums[nums[i] - 1] == nums[i]) {
return nums[i];
}
swap(nums, nums[i] - 1, i);
}
}
throw new RuntimeException();
}
public void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
二分法
考虑到题目要求不能更改原数组,且时间复杂度小于
O(n^2)
,因此暴力法和替换法均不满足要求。
采用二分法解答的关键:对要定位的
中位数
做二分,而不是对数组索引做二分。要定位的
中位数
的范围为
[1, n]
,这样每一次二分都可以将搜索区间缩小一半。
比如数组
[1,3,4,2,2]
。
class Solution {
public int findDuplicate(int[] nums) {
int left = 1;
int right = nums.length;
while(left right) {
int mid = (left + right) 1;
int count = 0;
for(int i = 0; i nums.length; i ++) {
if(nums[i] = mid) count ++;
}
if(count mid) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
}
参考资料
原文始发于微信公众号(xiaogan的技术博客):