(https://leetcode-cn.com/problems/find-the-duplicate-number/)
题目:给定一个包含 n + 1个整数的数组 nums,其数字都在1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。//牢记高亮部分
1:二分查找:
这里的二分是对1~n 进行二分,主要思想是我们在1~n中进行二分取值,设中间的数为mid,如果在nums数组中<= mid的数大于了mid,那么答案肯定在1~mid中,否则答案就在mid+1 ~ n中。
为什么是这样?其实是题目限制的比较严格,用往抽屉里放香蕉的方法去说,nums[i] 表示第i根香蕉放在nums[i] 号抽屉里,按理说一个抽屉里只能放一个香蕉,我们的目的是找出哪个抽屉里多放了香蕉。我们从1~n号抽屉里抽了mid号抽屉,如果mid号抽屉和前边的抽屉里的香蕉和大于了mid,那么1 ~mid号抽屉里肯定有某个抽屉多放了,因为如果不多放,1 ~ mid 号抽屉里的香蕉和最多为mid 个 。
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int n = nums.size(),left = 1,right = n-1,cnt;
while(left < right){
cnt = 0;
int mid = (left+right)/2;
for(int i = 0; i < n; i++) if(nums[i] <= mid) cnt++;
if(cnt > mid) right = mid;
else left = mid+1;
}
return left;
}
};
2:快慢指针:
在分析之前需要对 Floyd 判圈算法(又称龟兔赛跑算法)有所了解,如果不知道,可看我的这篇随笔https://www.cnblogs.com/Beic233/p/12965361.html
看完之后,我们就可以把nums数组给化成一个链表,nums[i] 就表示i号链表顶点的next指针指向nums[i] 号顶点,这时候再看一眼题目,题目保证了除了答案所求的顶点外,其他顶点肯定是一进一出,所以我们这个链表必有环,而答案就是这个环的起点(这里可以画画)。而下面的步骤对于了解 Floyd 判圈算法的你就非常简单了
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int fast = 0,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 fast;
}
};