弗洛伊德的烏龜和兔子--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;
    }
};

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