数据流中的中位数:堆排序

题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

如果数据在容器中已经排序,那么中位数可以由P1P2指向的数得到,如果容器中数据的数目是奇数,那么P1P2指向同一个数据。如下图所示:


如图整个数据容器分隔成两个部分,位于容器左边部分的数据比右边的数据小,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]
    }


};

posted @ 2018-03-29 20:54  依然有清风  阅读(540)  评论(0编辑  收藏  举报