【劍指Offer】6三、數據流中的中位數

  題目描述:java

  如何獲得一個數據流中的中位數?若是從數據流中讀出奇數個數值,那麼中位數就是全部數值排序以後位於中間的數值。若是從數據流中讀出偶數個數值,那麼中位數就是全部數值排序以後中間兩個數的平均值。咱們使用Insert()方法讀取數據流,使用GetMedian()方法獲取當前讀取數據的中位數。編程

  解題思路:數組

  首先要正確理解此題的含義,數據是從一個數據流中讀出來的,所以數據的數目隨着時間的變化而增長。對於從數據流中讀出來的數據,固然要用一個數據容器來保存,也就是當有新的數據從流中讀出時,須要插入數據容器中進行保存。那麼咱們須要考慮的主要問題就是選用什麼樣的數據結構來保存數據結構

  方法一:用數組保存數據。數組是最簡單的數據容器,若是數組沒有排序,在其中找中位數可使用類比快速排序的partition函數,則插入數據須要的時間複雜度是O(1),找中位數須要的複雜度是O(n)。除此以外,咱們還能夠想到用直接插入排序的思想,在每到來一個數據時,將其插入到合適的位置,這樣可使數組有序,這種方法使得插入數據的時間複雜度變爲O(n),由於可能致使n個數移動,而排序的數組找中位數很簡單,只須要O(1)的時間複雜度。函數

  方法二:用鏈表保存數據。用排序的鏈表保存從流中的數據,每讀出一個數據,須要O(n)的時間找到其插入的位置,而後能夠定義兩個指針指向中間的結點,能夠在O(1)的時間內找到中位數,和排序的數組差很少。指針

  方法三:用二叉搜索樹保存數據。在二叉搜索樹種插入一個數據的時間複雜度是O(logn),爲了獲得中位數,能夠在每一個結點增長一個表示子樹結點個數的字段,就能夠在O(logn)的時間內找到中位數,可是二叉搜索樹極度不平衡時,會退化爲鏈表,最差狀況仍須要O(n)的複雜度。code

  方法四:用AVL樹保存數據。因爲二叉搜索樹的退化,咱們很天然能夠想到用AVL樹來克服這個問題,並作一個修改,使平衡因子爲左右子樹的結點數之差,則這樣能夠在O(logn)的時間複雜度插入數據,並在O(1)的時間內找到中位數,可是問題在於AVL樹的實現比較複雜。blog

  方法五:最大堆和最小堆。咱們注意到當數據保存到容器中時,能夠分爲兩部分,左邊一部分的數據要比右邊一部分的數據小。以下圖所示,P1是左邊最大的數,P2是右邊最小的數,即便左右兩部分數據不是有序的,咱們也有一個結論就是:左邊最大的數小於右邊最小的數
排序




  所以,咱們能夠有以下的思路: 用一個最大堆實現左邊的數據存儲,用一個最小堆實現右邊的數據存儲,向堆中插入一個數據的時間是O(logn),而中位數就是堆頂的數據,只須要O(1)的時間就可獲得。

  而在具體實現上,首先要保證數據平均分配到兩個堆中,兩個堆中的數據數目之差不超過1,爲了實現平均分配,能夠在數據的總數目是偶數時,將數據插入最小堆,不然插入最大堆。it

  此外,還要保證全部最大堆中的數據要小於最小堆中的數據。因此,新傳入的數據要和最大堆中最大值或者最小堆中的最小值比較。當總數目是偶數時,咱們會插入最小堆,可是在這以前,咱們須要判斷這個數據和最大堆中的最大值哪一個更大,若是最大值中的最大值比較大,那麼將這個數據插入最大堆,並把最大堆中的最大值彈出插入最小堆。因爲最終插入到最小堆的是原最大堆中最大的,因此保證了最小堆中全部的數據都大於最大堆中的數據。

  總結:






  編程實現(Java):

import java.util.*;
public class Solution {
    /*
    思路:最大堆和最小堆
    */
    PriorityQueue<Integer> minHeap=new PriorityQueue<>();
    PriorityQueue<Integer> maxHeap=new PriorityQueue<>(new Comparator<Integer>(){
        public int compare(Integer o1,Integer o2){
            return o2-o1;
        }
    });
    int count=0;
    public void Insert(Integer num) {
        count++;
        if(count%2==0){   //偶數,插入最小堆
            if(!maxHeap.isEmpty() && num<maxHeap.peek()){ //若是num小於最大堆,那麼先插入最大堆
                maxHeap.add(num);
                num=maxHeap.poll();
            }
            minHeap.add(num);
        }else{ //奇數,插入最大堆
            if(!minHeap.isEmpty() && num>minHeap.peek()){
                minHeap.add(num);
                num=minHeap.poll();
            }
             maxHeap.add(num);
        }
    }

    public Double GetMedian() {
        if(minHeap.size()==maxHeap.size())
            return (minHeap.peek()+maxHeap.peek())/2.0;
        else if(maxHeap.size()>minHeap.size())
            return maxHeap.peek()/1.0;
        else
            return minHeap.peek()/1.0;
    }

}
相關文章
相關標籤/搜索