最大堆和最小堆思想的应用——数据流中的中位数问题

1 概念

最大堆:堆顶的key是堆里所有关键字中最大者;
最小堆:堆顶的key是堆里所有关键字中最小者;
堆是一棵完全二叉树,树中结点的值总是不大于(即最小堆)或者不小于(即最大堆)其孩子结点的值,所以每一个结点的子树也是一个堆。
根据最大堆和最小堆的思想,我们可以实现标准库容器:优先队列。

2 中位数可使用的堆思想

中位数:若奇数,则是中间位置的数字;若偶数,则是中间两个数字的平均值。
因此,整个数字可以分为两部分:一部分数据(小值堆)均比另一部分数据(大值堆)小。因此,将小值堆设置为最大堆,大值堆设置为最小堆,将数字平均分配到两个堆中,获取两个堆中的最值即可实现。

3 实现方案

每获得一个数据,先插入到最大堆中,然后将最大堆中的最大值插入到最小堆,就保证了最小堆中的所有数字大于最大堆的所有数字。

4 代码

常用的两种仿函数:升序greater<T>()和降序less<T>()
push_heap(_Iter,_Iter,_Compare):其中_Compare:对应上面的两种仿函数,其中greater是指树越向下越大,即最小堆,同理另一种是大顶堆。
常用操作:对于vector而言,首先数组push_back插入元素,然后再调用push_heap,它会使最后一个元素插到合适位置;而pop_heap之后pop_back则会将堆顶元素(即容器的第一个位置)和容器的最后一个位置交换位置,随后删除这个原来堆顶那个最值元素。

class Solution {
    
    
public:
    void Insert(int num)
    {
    
    
        if( (min.size() + max.size()) % 2 != 0)
        {
    
    
            if(max.size() > 0 && num < max[0])//先在最大堆(小值堆)里面找到最大值
            {
    
    
                max.push_back(num);
                push_heap(max.begin(),max.end(),less<int>());
                
                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
        {
    
    
            if(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()
    {
    
    
        //if(min.size() + max.size() == 0)
        //    return 0;
        if((min.size() + max.size()) % 2 == 0)
            return min[0];
        else
            return (min[0] + max[0]) / 2;
    
    }
private:
    vector<int> min;
    vector<int> max;

};

在C++的STL种还有优先队列这个适配器,也可以如下操作:

class Solution {
    
    
private:
    priority_queue<int, vector<int>, less<int> > p;
    priority_queue<int, vector<int>, greater<int> > q;
     
public:
    void Insert(int num)
    {
    
    
        if(p.empty() || num <= p.top()) p.push(num);
        else q.push(num);
        if(p.size() == q.size() + 2) q.push(p.top()), p.pop();
        if(p.size() + 1 == q.size()) p.push(q.top()), q.pop();
    }
    
    double GetMedian()
    {
    
     
      return p.size() == q.size() ? (p.top() + q.top()) / 2.0 : p.top();
    }
};

猜你喜欢

转载自blog.csdn.net/weixin_42979679/article/details/102789740
今日推荐