LeetCode:295 數據流的中位數 雙堆法

中位數是有序列表中間的數。如果列表長度是偶數,中位數則是中間兩個數的平均值。

例如,
[2,3,4] 的中位數是 3
[2,3] 的中位數是 (2 + 3) / 2 = 2.5

設計一個支持以下兩種操作的數據結構:
void addNum(int num) - 從數據流中添加一個整數到數據結構中。
double findMedian() - 返回目前所有元素的中位數。

示例:

addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3) 
findMedian() -> 2

進階:
如果數據流中所有整數都在 0 到 100 範圍內,你將如何優化你的算法?
如果數據流中 99% 的整數都在 0 到 100 範圍內,你將如何優化你的算法?

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/find-median-from-data-stream
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

思路

雙堆法:
關於STL的堆
使用一個大頂堆big和一個小頂堆small,存放元素,使得他們滿足

  • big的堆頂小於small的堆頂(左邊的最大值小於右邊的最小值
  • big和small堆中元素個數之差不超過1

如果在插入元素的時候發生【big和small堆中元素個數之差超過1】的情況,需要將元素多的堆的堆頂移動到元素少的堆中做新堆頂,類似AVL樹

我們總是刻意地,且方便地(這就是用堆的原因)取兩邊的最大/小值,以此決定新的元素應該插入在哪邊,新元素大於左邊big堆堆頂,那麼插入到small堆中,反之

  1. 如果兩堆元素之數量和爲偶數,那麼根據性質2,他們兩邊有相同個數的元素,中位數就是big的堆頂和small的堆頂的平均
  2. 如果兩堆元素數量之和爲奇數,那麼中位數位於元素多的堆的堆頂

代碼

class MedianFinder {
public:
    vector<int> big;
    vector<int> small;
    /** initialize your data structure here. */
    MedianFinder()
    {
        
    }
    
    void addNum(int num)
    {
        int lmax = (big.size()==0)?(INT_MAX):(big[0]);
        if(lmax<num)
        {
            small.push_back(num);
            push_heap(small.begin(), small.end(), greater<int>());
        }
        else
        {
            big.push_back(num);
            push_heap(big.begin(), big.end(), less<int>());
        }
        if(fabs(big.size()-small.size())>1)
        {
            if(big.size()>small.size())
            {
                small.push_back(big[0]);
                push_heap(small.begin(), small.end(), greater<int>());
                pop_heap(big.begin(), big.end(), less<int>());
                big.pop_back();
            }
            else if(small.size()>big.size())
            {
                big.push_back(small[0]);
                push_heap(big.begin(), big.end(), less<int>());
                pop_heap(small.begin(), small.end(), greater<int>());
                small.pop_back();
            }
        }
    }
    
    double findMedian()
    {
        if((big.size()+small.size())%2==0)
            return (double)(big[0]+small[0])/2;
        return (big.size()>small.size())?(big[0]):(small[0]);
    }
};

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder* obj = new MedianFinder();
 * obj->addNum(num);
 * double param_2 = obj->findMedian();
 */
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章