题目描述
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例 1:
输入: [1,3,4,2,2]
输出: 2
示例 2:
输入: [3,1,3,4,2]
输出: 3
说明:
不能更改原数组(假设数组是只读的)。
只能使用额外的 O(1) 的空间。
时间复杂度小于 O(n^2) 。
数组中只有一个重复的数字,但它可能不止重复出现一次。
题解
本题目限定了元素的取值范围"其数字都在 1 到 n 之间(包括 1 和 n)"。
另外,“说明”里的也特别重要~
说明:
> 不能更改原数组(假设数组是只读的)。
> 只能使用额外的 O(1) 的空间。
> 时间复杂度小于 O(n^2) 。
> 数组中只有一个重复的数字,但它可能不止重复出现一次。
最后解决的思路很简单,就是使用双指针,直接看代码注释即可。
代码
class Solution {
public int findDuplicate(int[] nums) {
// 如果数组就只有两个元素,直接返回即可
if(nums.length <= 2) { return nums[0]; }
int q = 0, s = 0;
int indexStart = 0; // indexStart用以记录起步位置
// 初始化q、s应该指向[第一个index!=nums[index]的元素],不应该为0
for(int index = 0; index < nums.length; index++) {
if(index != nums[index]) {
q = index;
s = index;
indexStart = index; // 保存起步位置
break;
}
}
// 快慢指针从同一位置(index)起步
while(true) {
// 快指针q的步长=2
q = nums[nums[q]];
// 慢指针s的步长=1
s = nums[s];
// 两指针相遇,结束第一次循环
if(q == s) {
// 慢指针回到初始起步点indexStart
s = indexStart;
break;
}
}
// 快慢指针继续一起移动,但要修改快指针的步长
// 快慢指针再相遇的元素,即所要的结果
while(true) {
// 快指针q的步长=1
q = nums[q];
// 慢指针s的步长=1
s = nums[s];
// 两指针相遇,结束循环
if(q == s) { break; }
}
return s;
}
}
提交
题后
今天这“每日一题”和《【LeetCode】面试题03. 数组中重复的数字》有些类似。
不同的是,今天这题限定了一些特别的条件,比如:
- 不能更改原数组(假设数组是只读的)
- 只能使用额外的 O(1) 的空间
- **时间复杂度小于 O(n^2) **
尤其是对复杂对的要求,这就提高了算法设计的难度。
我想着各种优化方法,很艰难地,最后找到了这种。
哇地一声就哭出来了,太难了/(ㄒoㄒ)/~~