2020-05-26 LeetCode 287 尋找重複數 C

題目:

 給定一個包含 n + 1 個整數的數組 nums,其數字都在 1 到 n 之間(包括 1 和 n),可知至少存在一個重複的整數。假設只有一個重複的整數,找出這個重複的數。

示例1:

輸入: [1,3,4,2,2]
輸出: 2

示例2:

輸入: [3,1,3,4,2]
輸出: 3

說明:

  1. 不能更改原數組(假設數組是隻讀的)。
  2. 只能使用額外的 O(1) 的空間。
  3. 時間複雜度小於O(n2)O(n^2)
  4. 數組中只有一個重複的數字,但它可能不止重複出現一次。

題目本身不難,解法太多了,難就難在說明上,大概翻譯一下說明的意思

  1. 數組排序和符號翻轉的方法不能用
  2. HashMap和額外數組計數不能用
  3. 暴力算法不可取
  4. 異或解法不能用

 第四條可把我坑慘了,想了半天用異或怎麼解,硬是沒找到規律,後面從算法複雜度入手想,小於O(n2)O(n^2)的意思是O(nlogn)O(nlogn)也是可以的,就想到了二分的解法,數字是1到n之間,長度爲n+1,每多一個重複的數一定會少一個其他數,這樣在重複數x的附近一定滿足這樣的規律:用cnt(i)表示數組中比i小的數的數量,若i<x,則cnt(i)<i,若i>=x,則cnt(i)>i,用這樣的規律去二分查找就好了,每次計算cnt(i)需要遍歷整個數組,所以整體時間複雜度是O(nlogn)O(nlogn)

代碼:

int findDuplicate(int* nums, int numsSize){
    int l=1,r=numsSize,mid;
    int cnt,ans;
    while(l<=r){
        mid=(l+r)/2;
        int i;
        cnt=0;
        for(i=0;i<numsSize;i++){
            if(nums[i]<=mid)
                cnt++;
        }
        if(cnt>mid){
            ans=mid;
            r=mid-1;
        }
        else
            l=mid+1;
    }
    return ans;
}

運行結果:

在這裏插入圖片描述

 上面就是題解一的思路,官方題解第二種解法屬實太抽象看不懂,後邊主要研究的是Floyd判圈算法,這個讓我想到了小學的一道奧數題,印象中是一摸一樣的。基本思路是將數組抽象爲一個圓環,因爲1~nn 之間有n+1個數,所以一定有重複數字出現,而重複的數字即是圓環與線的交匯點。然後設置兩個指針fast和slow,快指針一次走兩步,慢指針一次走一步。當兩個指針第一次相遇時,令快指針回到起點且也變成一次走一步,慢指針則繼續前進,再次回合時即是線與圓環的交匯點。

 關鍵點就是爲什麼第二次可以相遇,以及爲什麼相遇處就是重複數字。官方推導如下

在這裏插入圖片描述

 在k=1的情況下,起點到入口的距離就是第一次相遇點到入口的距離,k>1也只是多了幾整圈,所以一起出發必相遇,而且必在入口處。

代碼:

int findDuplicate(int* nums, int numsSize){
    int slow=nums[0],fast=nums[nums[0]];
    //第一次相遇
    while(slow!=fast)
    {
    	//讓慢指針每次移動一位,快指針每次移動兩位
        slow=nums[slow];
        fast=nums[nums[fast]]; 
    }
    //讓快指針回到起點
    fast=0;
    //讓慢指針和快指針速度一致,找環的入口
    while(slow!=fast)
    {
        slow=nums[slow];
        fast=nums[fast];
    }
    return slow;
}

運行結果:

在這裏插入圖片描述

 mark,mark,mark,tql

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