leetcode學習日記9-尋找重複數

leetcode學習日記9-尋找重複數

題目描述

給定一個包含 n + 1 個整數的數組 nums,其數字都在 1 到 n 之間(包括 1 和 n),可知至少存在一個重複的整數。假設只有一個重複的整數,找出這個重複的數。
輸入: [1,3,4,2,2]
輸出: 2
說明:
不能更改原數組(假設數組是隻讀的)。
只能使用額外的 O(1) 的空間。
時間複雜度小於 O(n2) 。
數組中只有一個重複的數字,但它可能不止重複出現一次。

初步探索

首先,最簡單的一種思路是兩兩比較是否相等,如果相等就輸出。然而,由於O(n2) 的限制,不能用這種算法。

其次,又想到可以利用這些數字都是1到n中,可以將他們相加,然後通過和1到n的總和比較得出相差多少。然而,這個算法也又缺陷,因爲由於數組中重複的數字可能重複多次,難以做到準確判斷。

最後,由於時間複雜度的限制,想到可能可以使用二分搜索法。但是不能進行排序,因爲原數組不可改變,也不能使用O(n)的額外空間。

綜合方法

如何將這些思考綜合起來呢?
我想到即使沒有排序,由於數字的範圍是固定的,可以使用一種變相的二分法。模糊的思路是,如果這個數組是恰好1,2,3…n,那麼這個數組可以看成是"平衡"的,因爲每個數字都出現了一次,其中位數是在(1+n)/2的地方。然而,如果某一個數字出現了兩次,那麼會有兩種情況

  • 這個數字小於中位數,那麼我們將數組排列後,新的中位數將會小於原本的中位數
  • 這個數字大於中位數,則同理,新的中位數將大於原本的中位數
  • 這個數字等於中位數,則中位數重複

是不是有點像二分法?這裏,我們並不需要排序數組,只需要判斷理論和實際的中位數的位置就可以逐步縮小搜索範圍了。

  • 如果新的中位數小於原本中位數,則重複數字小於原本中位數
  • 同理可得新的中位數大於原本中位數的情況
  • 中位數重複,那麼重複的數字就是是中位數

如何找中位數的位置呢?我的方法是

  1. 找到理論中位數,即左邊界l和右邊界r的平均, mid=(l+r)/2
  2. 遍歷數組,統計小於中位數的數字個數 smaller,大於中位數的數字個數 bigger,等於中位數的數字個數equal
  3. 如果equal>2,說明mid就是我們要找的數字
  4. 如果smaller > bigger, 則將搜索範圍縮小到 l到mid,即將r賦值爲floor(mid),smaller<bigger同理
  5. 重複1-4

細節

注意,在數學定義中,如果有偶數個數字,中位數則是中間兩個數字的平均數。在這道題中,也必須延用這樣的定義,而不能簡單把mid定義爲int,使其向下取整。與一般二分搜索不同的是,我們需要考慮大於和小於的數字個數。如果數列有偶數個數字,向下取整後的中位數會使得一個已經平衡的數列變得不平衡,因爲原本小於中位數的數字被算作等於了。所以,最簡單的解決方法就是使用double定義mid。當然,這個中位數的0.5在邊界時是沒有意義的,故在縮小搜索範圍時可以用floor和ceiling函數變爲整數。

代碼



class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int n=nums.size()-1;
        int l=1;// left boundary
        int r=n;// right boundary: both are representing numbers but not indexes
        int ans=0;//solution
        while(1){
            int bigger=0;//the count of numbers bigger than the median
            int smaller=0;// the count of numbers smaller than the median
            int equal=0;// the count of numbers equal to median
            
            double mid=(l+r)*0.5;//calculate median
            int i=0;
            for(i=0;i<=n;i++)
            {
                if(nums[i]>mid&&nums[i]<=r) bigger++;
                if(nums[i]<mid&&nums[i]>=l) smaller++;
                if(nums[i]==mid) equal++;
            }
            if(equal>=2){
                ans=mid;// answer found!
                break;
            }
            if(bigger>smaller) l=ceil(mid);// modify the boundary
            if(bigger<=smaller) r=floor(mid);
            
        }
        return ans;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章