寻找重复数
题目描述
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。
你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。
输入:nums = [1,3,4,2,2]
输出:2
输入:nums = [3,1,3,4,2]
输出:3
解题思路1:二分查找
为什么可以应用二分查找呢?它又不是按照顺序进行排列
此时可以根据它的取值范围进行二分查找,取[1, n]中间数,然后来计数nums中比他小的个数有多少,因为比mid小的只能是有mid个如果多于这个数则代表重复的数在左边这部分,否则则在右边,然后依次进行划分,跳出循环不满足左小于右则直接返回左几颗
代码1
class Solution {
public int findDuplicate(int[] nums) {
int len = nums.length; // n + 1 = len, n = len - 1
// 在 [1..n] 查找 nums 中重复的元素
int left = 1;
int right = len - 1;
while (left < right) {
int mid = (left + right) / 2;
// nums 中小于等于 mid 的元素的个数
int count = 0;
for (int num : nums) {
if (num <= mid) {
count++;
}
}
if (count > mid) {
// 下一轮搜索的区间 [left..mid]
right = mid;
} else {
// 下一轮搜索的区间 [mid + 1..right]
left = mid + 1;
}
}
return left;
}
}
解题思路2:快慢指针
建立索引与值的映射关系的链表。
利用快慢指针的关键是,检测环以及找到环的起始点
检测环容易一点,快指针走两步慢指针走一步,直到两只相遇,即为有环,找到环的入口用到一些数学知识
-
设数组的起始位置到环的起始位置的距离为a。
-
设环的起始位置到相遇点的距离为b。
-
设相遇点到环的起始位置的距离为c。
当两个指针(pre1,pre2)相遇时。 -
此时先假设快指针(pre1)走了一圈就相遇,即:a+2b+c,那么慢指针走过的距离为:a+b;
由于快指针依次走两步,假设其速度为2,慢指针为1,相同时间到达同一点,建立等式:(a+b)/1=(a+2b+c)/2 解等式即可得到a=c。
那么此时只需要让一个指针从开始0位置以速度为1走,慢指针继续原来位置原来速度走,相遇时就是环入口位置。 -
换一种情况:当慢指针到达b时,快指针已经在环中走了k圈,建立等式:(a+b)/1=(a+(k+1)b+kc)/2
解等式得到:a=(k-1)b+kc。这个等式代表从相遇点走k-1圈后再走c的距离等于a。那这就说明了慢指针继续原来位置原来速度走,相遇时就是环入口位置。
代码2
class Solution {
public int findDuplicate(int[] nums) {
int slow = 0;
int fast = 0;
slow = nums[slow];
fast = nums[nums[fast]];
while(slow != fast){
slow = nums[slow];
fast = nums[nums[fast]];
}
int pre1 = 0;
int pre2 = slow;
while(pre1 != pre2){
pre1 = nums[pre1];
pre2 = nums[pre2];
}
return pre1;
}
}