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

来源:互联网 发布:知乎名字的由来 编辑:程序博客网 时间:2024/04/26 07:34

整理自剑指Offer

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


一:题目描述

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


二:解题思路

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

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

N1N2...N2mN2m+1

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

N1N2...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();        }};

原创粉丝点击