乌龟和兔子的算法我记得最早是用在判断链表是否有循环,但是实际上今天做了一道题之后,可以总结为,判断一些循环的情况,都可以用这个方法,首先看看题目
287. 寻找重复数
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例 1:
输入: [1,3,4,2,2]
输出: 2
示例 2:
输入: [3,1,3,4,2]
输出: 3
暴力和排序的方法就不说了,先说一个也很巧妙的方法:
1. 二分法:
假设一下如果不重复的话(1,2,3,4,5),用二分的方式,左边的从1到中位数mid = 3(nums[i]<=mid)的个数就应该等于3,但是如果,左边多出来了一个重复的数,那么绝对就大于mid = 3,如(1,2,2,3,5,6)的个数就为4,故可知重复的数在左边,反之在右边,逐渐的缩小范围,直到只剩下一个数!
代码如下:
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int n = nums.size()-1;
int l = 1;
int r = n;
while(l < r){
int mid = (l+r)>>1;
int count = 0;
for(int i:nums){
if(i <= mid) count++;
}
if(count > mid) r = mid;
else l = mid+1;
}
return l;
}
};
2.乌龟与兔子法:
接下来进入正题,为什么这题可以用乌龟和兔子的方法?
因为这题存在着循环!!!
怎么看有没有循环?方法如下:
乌龟和兔子都代表两个指针,假设都指向下标为0的数nums[0],指针移动的方向为,当前的值nums[i],即跳到下标为nums[i]的位置,值为nums[nums[i]]乌龟每次跳一步,兔子跳两步。最后乌龟和兔子肯定会相遇!!!,因为总会有两个下标指向的是同一个数字!!!
如 ;
(0,1,2,3,4)
(1,3,4,2,2)
每次下一步的方向为:
1->3->2->4->2->4…循环了!!!
但是这题主要找的是循环入口!!!这就要想起了如何求链表的循环入口了。
这里讲的很好:
寻找链表的循环入口
代码如下:
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int tortise = nums[0];
int rabbit = nums[0];
do{
tortise = nums[tortise];
rabbit = nums[nums[rabbit]];
}while(tortise != rabbit);
int ptr1 = nums[0];
int ptr2 = tortise;
while(ptr1 != ptr2){
ptr1 = nums[ptr1];
ptr2 = nums[ptr2];
}
return ptr1;
}
};