【剑指Offer】面试题64:数据流中的中位数

整理自剑指Offer

牛客网https://www.nowcoder.com/questionTerminal/9be0172896bd43948f8a32fb954e1be1


一:题目描述

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


二:解题思路

由于数据从一个数据流中读出来的,数据的数目随着时间的变化而增加,如何选择一个容器存储数据,使插入一个数据后有序 与 计算中位数的时间复杂度性能最优?

如果利用两个指针去寻找中位数

N1 N2 ... N2m N2m+1

奇数:P1与P2指向同一个位置

N1 N2 ... N2m

偶数:P1,P2指向中间的两个位置(排序后)


整个容器被分成两部分,位于容器左部分的数据比右边的小

P1指向数据左部分最大的数

P2指向数据右边最小的数


如果能够保证数据容器左边的数据小于右边的数据,这样即使左右两边的数据没有排序,也可以根据左边最大的数及右边最小的数得到中位数。

如何快速从一个容器中找到最大的数?----大顶堆实现这个数据容器

如何快速从一个容器中找到最小的数?---小顶堆


要考虑的细节问题:

1.保证数据平均分配在两个堆中:因此两个堆中数目之差不能超过1

2.要保证大顶堆中所有的数据小于小顶堆中的数据


三:代码实现

大顶堆-小顶堆

class Solution {
public:
 
    vector<int> max;//存放大顶堆
    vector<int> min; //存放小顶堆
    
    bool isInvalidInput=false;
    
    //当前数据总数为偶数时,插入小顶堆,奇数时,插入大顶堆--保证大顶堆与小顶堆数据平衡
    //插入大顶堆前,现将数据插入小顶堆,再将最小值插入到大顶堆中,保证大顶堆元素小于小顶堆元素
    //同理,插入小顶堆的元素,现将数据插入到大顶堆,再将最大值插入到小顶堆中,保证小顶堆的元素大于大顶堆元素
    

    void Insert(int num){
        //偶数-插入小顶堆
        if((min.size()+max.size())%2==0){
            
            //将数据插入大顶堆,找到大顶堆中最大的元素
            if(max.size()>0 && num<max[0]){
                // push_heap (_First, _Last),要先在容器中加入数据,再调用push_heap ()
                max.push_back(num);//先将元素压入容器
                push_heap(max.begin(),max.end(),less<int>());//调整最大堆 
                num=max[0];//取出最大堆的最大值 
                //pop_heap(_First, _Last),要先调用pop_heap()再在容器中删除数据
                pop_heap(max.begin(),max.end(),less<int>());//删除最大堆的最大值
                max.pop_back(); //在容器中删除
            }
            
            //如果max为空 或者 num大于大顶堆最大值,则将元素直接插入小顶堆
            min.push_back(num);//插入小顶堆
            push_heap(min.begin(),min.end(),greater<int>());//调整小顶堆
        }//if
        else{
        //奇数--插入大顶堆
            if(min.size()>0 && num>min[0]){
                // push_heap (_First, _Last),要先在容器中加入数据,再调用push_heap ()
                min.push_back(num);//先压入小顶堆
                push_heap(min.begin(),min.end(),greater<int>()); //调整小顶堆
                num=min[0];  //获得小顶堆最小值
                //pop_heap(_First, _Last),要先调用pop_heap()再在容器中删除数据
                pop_heap(min.begin(),min.end(),greater<int>());  //删除小顶堆最小值
                min.pop_back(); //从容器中删除
            }
            //如果小顶堆为空,或者num小于小顶堆最小值
            max.push_back(num); //直接插入大顶堆
            push_heap(max.begin(),max.end(),less<int>());//调整大顶堆
        }
    }
     double GetMedian(){
         int size=min.size()+max.size();
         if(size<=0)  //没有元素,抛出异常
         {
             isInvalidInput=true;
             return 0;    //throw exception("No numbers are available");
         }
             
         if(size%2==0)//偶数
             return (max[0]+min[0])/2.0;
         else
             return min[0];
     }

};


优先队列的方式实现

class Solution {
public:
    
    priority_queue<int, vector<int>, less<int> > max;  //优先队列,less保证队列中元素按照从大到小排列,即队首元素最大
    priority_queue<int, vector<int>, greater<int> > min;  //有限队列,greater保证队列中元素按照从小到大排列,即队首元素最小
   
    void Insert(int num)
    {
        if(max.empty()||num<=max.top())
            max.push(num);
        else
            min.push(num);
        
        //保证max与min中元素个数均衡
        if(max.size()==min.size()+2){
            min.push(max.top());
            max.pop();
        }
        //如果元素个数为奇数,保证max的队首元素为中位数
        if(max.size()+1==min.size()){
            max.push(min.top());
            min.pop();
        }
            
        
    }

    double GetMedian()
    { 
        return max.size()==min.size()?(max.top()+min.top())/2.0 : max.top();
    
    }

};

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章