弗洛伊德的乌龟和兔子--leecode刷题

乌龟和兔子的算法我记得最早是用在判断链表是否有循环,但是实际上今天做了一道题之后,可以总结为,判断一些循环的情况,都可以用这个方法,首先看看题目

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;
    }
};

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章