【劍指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();
    
    }

};

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