题目描述:
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
思路:
数据流中的数据需要用合适的容器存放。
方法1:用数组
用数组存放是比较直接的方法,将新数据插入一个数组的时间复杂度是O(1),从中找中位数可以用partition的方法(参考:数组中出现次数超过一半的数字),时间复杂度是O(n)。但是数组有个问题是必须事先指定长度。
方法2:用堆
数据被中位数分为两半,用一个最大堆和一个最小堆,各存放一半的数字,且最大堆中数字都小于最小堆中数字。则最大堆存放的是中位数左边的数字,最小堆中存放的是中位数右边的数字。
具体实现的时候,为了保证两个堆中数字平均分配,可以在数据总数为偶数时将新数据放进最小堆中,数据总数为寄数时将新数据放进最大堆中。但是要保证新数据放进去之后最大堆中的数字都比最小堆中数字小,所以在将新数据放进最小堆之前,先要判断新数据是不是比最大堆中数据都大,最大堆堆顶的数据是最大的数据,要是新数据比堆顶数据还大,就可以直接放入最小堆,要是新数据比堆顶数据小,那就先把新数据放进最大堆,然后把新的最大堆中的最大数取出来放进最小堆。将新数据放入最大堆之前也要进行类似的判断。
注意:我们采用vector容器和stl中的函数push_heap,pop_heap实现堆,用less和greater函数实现最大最小堆。
每次插入元素后调用push_heap(maxHeap.begin(),maxHeap.end(),less<int>())对插入的元素做堆排序,默认使用less<int>表示将最大元素放在第一个位置。如果要删除堆顶元素,先调用pop_heap(maxHeap.begin(),maxHeap.end(),less<int>()),将堆顶元素与end前的元素(即最后一个元素)交换,然后将除了最后一个元素之外的元素重新进行堆排序。
参考代码:
在线测试
AC代码
class Solution {
public:
void Insert(int num)
{
if(((maxHeap.size()+minHeap.size())&1)==0)
{
if(!maxHeap.empty()&&num<maxHeap[0])
{
maxHeap.push_back(num);
push_heap(maxHeap.begin(),maxHeap.end(),less<int>());
num=maxHeap[0];
pop_heap(maxHeap.begin(),maxHeap.end(),less<int>());
maxHeap.pop_back();
}
minHeap.push_back(num);
push_heap(minHeap.begin(),minHeap.end(),greater<int>());
}
else
{
if(!minHeap.empty()&&num>minHeap[0])
{
minHeap.push_back(num);
push_heap(minHeap.begin(),minHeap.end(),greater<int>());
num=minHeap[0];
pop_heap(minHeap.begin(),minHeap.end(),greater<int>());
minHeap.pop_back();
}
maxHeap.push_back(num);
push_heap(maxHeap.begin(),maxHeap.end(),less<int>());
}
}
double GetMedian()
{
if((maxHeap.size()+minHeap.size())&1)
{
return minHeap[0];
}
else
return (minHeap[0]+maxHeap[0])/2.0;
}
private:
vector<int> maxHeap;
vector<int> minHeap;
};