https://blog.csdn.net/whdAlive/article/details/80459730
二分法
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间,包括 1 和 n ,可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例 1:
输入: [1,3,4,2,2]
输出: 2
- 1
- 2
示例 2:
输入: [3,1,3,4,2]
输出: 3
- 1
- 2
说明:
- 不能更改原数组(假设数组是只读的)。
- 只能使用额外的 O(1) 的空间。
- 时间复杂度小于 O(n2) 。
- 数组中只有一个重复的数字,但它可能不止重复出现一次。
分析
由于限制了空间同时不能更改原数组,导致我们不能使用 第一个缺失的数字 中的解法,即元素按其值放入指定位置。
分析一下这道题,如果正常情况没有重复数字,那么从1~n只有n个数字,当有一个重复数字时,1~n有n+1个数字。这样我们就可以将n个数字从其中间数字m处分为两半,如果1~m中数目超过m,那么重复数字出现在1~m,反之则出现在m+1~n中。
这么一看,这不是典型的二分法么?
代码
public int findDuplicate(int[] nums) {
int len = nums.length;
return findDuplicateCore(nums, 1, len-1);
}
public int findDuplicateCore(int[] nums, int begin, int end) {
while (begin <= end) {
if(begin == end) {
return begin;
}
int mid = begin + (end - begin) / 2;
int count = count(nums, begin, mid);
if (count > (mid - begin + 1)) {
return findDuplicateCore(nums, begin, mid);
} else {
return findDuplicateCore(nums, mid + 1, end);
}
}
return -1;
}
public int count(int[] nums, int begin, int end) {
int ret = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] >= begin && nums[i] <= end) {
ret++;
}
}
return ret;
}
Java版快慢指针简单实现
- 将数组转化为链表形式:数组 [1,3,4,2,2]
current / index 0 1 2 3 4 next / num[index] 1 3 4 2 2 index
为当前值的索引,num[index]
为下个一值的索引next index
。上表中的数组表示成链表如下图,方框中为index, num[index]
- 利用【142_环形链表 II】的方法,找到环入口,即为重复数字
- 设:
slow
指针移动速度为1,fast
指针移动速度为2;slow
指针在环内移动(非环部分)长度为a,slow
指针在环内移动长度为b - 两指针相遇时候,
slow
指针移动距离为a+b,fast
指针移动距离为2(a+b),可知两指针距离差a+b即为整数倍的环长 - 从head移动a的距离为入环点;由2可知从head开始移动a+(a+b)的距离也为入环点,即将A点继续移动距离a则可到达入环点
- 将
slow
指针移动回head,同时同速移动两个指针,相遇点即为入环点
- 设:
说明:因为数组中不含0,所以不会因为
index = 0, num[0] = 0
导致死循环;对于其他位置index = num[index]
,若该值重复则会自身成环,若无重复则不会被遍历到public class FindTheDuplicateNumber { public int findDuplicate(int[] nums) { if (nums.length <= 1) { return -1; } // 共同起点是0 int fast = nums[nums[0]]; int slow = nums[0]; // 第一次相遇 while (fast != slow) { slow = nums[slow]; fast = nums[nums[fast]]; } // 寻找入环点,注意起点是0 slow = 0; while (fast != slow) { slow = nums[slow]; fast = nums[fast]; } return fast; } }
扩展阅读:
每日一练:找出数组中的重复数字
https://blog.csdn.net/qq_41889292/article/details/80470860- 利用【142_环形链表 II】的方法,找到环入口,即为重复数字