中位數是有序列表中間的數。如果列表長度是偶數,中位數則是中間兩個數的平均值。
例如,
[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堆中,反之
- 如果兩堆元素之數量和爲偶數,那麼根據性質2,他們兩邊有相同個數的元素,中位數就是big的堆頂和small的堆頂的平均
- 如果兩堆元素數量之和爲奇數,那麼中位數位於元素多的堆的堆頂
代碼
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();
*/