给定一个包含 n + 1
个整数的数组 nums
,其数字都在 1
到 n
之间(包括 1
和 n
),可知至少存在一个重复的整数。
假设 nums
只有 一个重复的整数 ,找出 这个重复的数 。
你设计的解决方案必须不修改数组 nums
且只用常量级 O(1)
的额外空间。
示例 1:
输入:nums = [1,3,4,2,2]
输出:2
示例 2:
输入:nums = [3,1,3,4,2]
输出:3
示例 3:
输入:nums = [1,1]
输出:1
示例 4:
输入:nums = [1,1,2]
输出:1
提示:
· 1 <= n <= 10^5
· nums.length == n + 1
· 1 <= nums[i] <= n
· nums 中 只有一个整数 出现 两次或多次 ,其余整数均只出现 一次
解法1:二分查找
每次取一个1
到n
之间的数m
,然后统计整个数组内小于等于m的总数量count
,如果count
小于等于m
,则m
不是重复数,反之,m
可能是重复数或者小于m
的一个数为重复数,不断往左边递进,二分查找可以加快这个递进速度。
public class Solution {
public int findDuplicate(int[] nums) {
int left=1;
int right= nums.length-1;
while (left<right){
int mid=left+(right-left)/2;
int count=0;
for (int num:nums){
if (num<=mid){
count++;
}
}
if (count<=mid){
left=mid+1;
}else {
right=mid;
}
}
return left;
}
}
解法二:快慢指针
先明确前提,整数的数组 nums
中的数字范围是 [1,n]
。考虑一下两种情况:
-
如果数组中没有重复的数,以数组
[1,3,4,2]
为例,我们将数组下标 n 和数nums[n]
建立一个映射关系f(n)
,
其映射关系n->f(n)
为:
0->1
,1->3
,2->4
,3->2
我们从下标为0
出发,根据f(n)
计算出一个值,以这个值为新的下标,再用这个函数计算,以此类推,直到下标超界。这样可以产生一个类似链表一样的序列。
0->1->3->2->4->null
-
如果数组中有重复的数,以数组
[1,3,4,2,2]
为例,我们将数组下标n
和数nums[n]
建立一个映射关系f(n)
,
其映射关系n->f(n)
为:
0->1
,1->3
,2->4
,3->2
,4->2
同样的,我们从下标为0
出发,根据f(n)
计算出一个值,以这个值为新的下标,再用这个函数计算,以此类推产生一个类似链表一样的序列。
0->1->3->2->4->2->4->2->……
这里2->4
是一个循环,那么这个链表可以抽象为下图:
该链表即为有环链表,使用快慢指针即可找到重复数。
public class Solution {
public int findDuplicate(int[] nums) {
int fast=0;
int slow=0;
do {
fast=nums[nums[fast]];
slow=nums[slow];
}while (fast!=slow);
slow=0;
while (slow!=fast){
slow=nums[slow];
fast=nums[fast];
}
return slow;
}
}