剑指offer——数据流中的中位数

题目描述

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

思路 https://blog.csdn.net/moses1213/article/details/51332959

STL---heap概述  https://blog.csdn.net/moses1213/article/details/51332599

代码https://www.cnblogs.com/easonliu/p/4441916.html

https://www.nowcoder.com/profile/2156888/codeBookDetail?submissionId=13722304

 

作者用到的方法非常巧妙,注意到我们只需要位于中间的两个数就可以得出中位数,当元素个数为奇数个时可以看成这两个数一样。中位数在前半部分它是最大的,右半部分是最小的。我们只要始终知道前半部分数里的最大的那个数,后半部分里那个最小的数就可以了,其他的不用关心。这明显是堆的功用,前半部分用一个最大堆,后半部分建一个最小堆。中位数要保持两个堆的数目之差不超过1.为了实现平均分配,可以在数据的总数目是偶数时把新数据插入到最小堆中,否则插入最大堆中。

    另一个问题是保持最大堆的所有数据都小于最小堆中的数据。如果插入一个数使得总数目和为偶数,根据之前的原则应该插入最小堆中,但是新插入的元素可能比最大堆中的元素小怎么办?这就违反了最小堆的数据一定大于最大堆得原则。实际上这里插入的元素应该是新元素和最大堆中所有元素的最大值,最大堆正好有返回一个最大值的作用,所以新插入的元素先插入最大堆,然后取出最大值再插入最小堆中。如果插入元素使得数据总数目和为奇数,处理方法类似。

 

 

class Solution {
private:
     vector<int> min; //数组中的后一半元素组成一个最小化堆
     vector<int> max; //数组中的前一半元素组成一个最大化堆
public:
     void Insert(int num) {
         if(((min.size()+max.size()) & 1) == 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() { 
         int size=min.size() + max.size();
         if(size==0) return -1;
         double median = 0;
         if((size&1) != 0) {
             median = (double) min[0];
         } else {
             median = (double) (max[0] + min[0]) / 2;
         }
         return median;
     }
 };

 

更简洁的写法:

class Solution {
    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();
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值