数据流中的中位数:堆排序
题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
如果数据在容器中已经排序,那么中位数可以由P1和P2指向的数得到,如果容器中数据的数目是奇数,那么P1和P2指向同一个数据。如下图所示:
如图整个数据容器分隔成两个部分,位于容器左边部分的数据比右边的数据小,P1指向的数据是左边部分最大的数,P2指向的数据是右边部分最小的数。如果我们能够保证数据容器左边的数据都小于右边的数据,这样即使左右两边内部的数据没有排序,也可以根据左边最大数和右边最小数得到中位数。所以自然想到的是用一个最大堆实现左边的数据容器,用最小堆实现右边的数据容器。这样一来,可以在O(nlogn)时间内添加一个新结点,同时用O(1)时间得到我们需要的中位数。在分析上我们必须起码满足两个基本保证:一是保证数据平均分配到两个堆中,因此两个堆中数据的数目之差不能超过1,所以在数据总数目是偶数时把新数据插入到最小堆中或最大堆中;二是保证最大堆里的所有数据都小于最小堆中所有的数据。
代码:
class Solution {
private:
vector<int> max;
vector<int> min;
/*一是保证数据平均分配到两个堆中,因此两个堆中数据的数目之差不能超过1,
所以在数据总数目是偶数时把新数据插入到最小堆中或最大堆中;
二是保证最大堆里的所有数据都小于最小堆中所有的数据。*/
public:
void Insert(int num)
{
int size=max.size()+min.size();
if((size&1)==0)//当总数为偶数时,先插入最大堆,再插入最小堆,==优先于&
{
while(max.size()>0&&num<max[0])
{
max.push_back(num);
push_heap(max.begin(),max.end(),less<int>());//push_heap函数不需要前缀
num=max[0];
pop_heap(max.begin(),max.end(),less<int>());
max.pop_back();
}
min.push_back(num);
push_heap(min.begin(),min.end(),greater<int>());
}
else //当总数是奇数时,先插入最小堆,再插入最大堆
{
while(min.size()>0&&num>min[0])
{
min.push_back(num);
push_heap(min.begin(),min.end(),greater<int>());
num=min[0];
pop_heap(min.begin(),min.end(),greater<int>());
min.pop_back();
}
max.push_back(num);
push_heap(max.begin(),max.end(),less<int>());
}
}
double GetMedian()
{
int size=max.size()+min.size();
if(size<=0) return 0;
if((size&1)==0)//总数目为偶数,取中间两个数平均数
return (max[0]+min[0])/2.0;//除以2.0
else return min[0];//因为偶数的时候插入到最小堆,所以返回min[0]
}
};