【Java】 劍指offer(41) 數據流中的中位數

本文參考自《劍指offer》一書,代碼採用Java語言。html

更多:《劍指Offer》Java實現合集  java

題目 

  如何獲得一個數據流中的中位數?若是從數據流中讀出奇數個數值,那麼中位數就是全部數值排序以後位於中間的數值。若是從數據流中讀出偶數個數值,那麼中位數就是全部數值排序以後中間兩個數的平均值。ide

思路

  所謂數據流,就是不會一次性讀入全部數據,只能一個一個讀取,每一步都要求能計算中位數。函數

  將讀入的數據分爲兩部分,一部分數字小,另外一部分大。小的一部分採用大頂堆存放,大的一部分採用小頂堆存放。當總個數爲偶數時,使兩個堆的數目相同,則中位數=大頂堆的最大數字與小頂堆的最小數字的平均值;而總個數爲奇數時,使小頂堆的個數比大頂堆多一,則中位數=小頂堆的最小數字。post

  所以,插入的步驟以下:測試

  1.若已讀取的個數爲偶數(包括0)時,兩個堆的數目已經相同,將新讀取的數插入到小頂堆中,從而實現小頂堆的個數多一。可是,若是新讀取的數字比大頂堆中最大的數字還小,就不能直接插入到小頂堆中了 ,此時必須將新數字插入到大頂堆中,而將大頂堆中的最大數字插入到小頂堆中,從而實現小頂堆的個數多一。url

  2若已讀取的個數爲奇數時,小頂堆的個數多一,因此要將新讀取數字插入到大頂堆中,此時方法與上面相似。spa

 

測試算例 htm

  1.功能測試(讀入奇/偶數個數字)blog

  2.邊界值測試(讀入0個、1個、2個數字)

Java代碼

//題目:如何獲得一個數據流中的中位數?若是從數據流中讀出奇數個數值,那麼
//中位數就是全部數值排序以後位於中間的數值。若是從數據流中讀出偶數個數值,
//那麼中位數就是全部數值排序以後中間兩個數的平均值。

import java.util.PriorityQueue;
import java.util.Comparator;

public class StreamMedian {
    PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>(); //小頂堆,默認容量爲11
    PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(11,new Comparator<Integer>(){ //大頂堆,容量11
        public int compare(Integer i1,Integer i2){
            return i2-i1;
        }
    });
    public void Insert(Integer num) {
        if(((minHeap.size()+maxHeap.size())&1)==0){//偶數時,下個數字加入小頂堆
            if(!maxHeap.isEmpty() && maxHeap.peek()>num){
                maxHeap.offer(num);
                num=maxHeap.poll();
            }
            minHeap.offer(num);
        }else{//奇數時,下一個數字放入大頂堆
            if(!minHeap.isEmpty() && minHeap.peek()<num){
                minHeap.offer(num);
                num=minHeap.poll();
            }
            maxHeap.offer(num);
        }
    }

    public Double GetMedian() {
        if((minHeap.size()+maxHeap.size())==0)
            throw new RuntimeException();
        double median;
        if((minHeap.size()+maxHeap.size()&1)==0){
            median=(maxHeap.peek()+minHeap.peek())/2.0;
        }else{
            median=minHeap.peek();
        }
        return median;
    }
}

  

收穫

  1.最大最小堆能夠用PriorityQueue實現,PriorityQueue默認是一個小頂堆,經過傳入自定義的Comparator函數能夠實現大頂堆:

    PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(11,new Comparator<Integer>(){ //大頂堆,容量11
        @Override
    	public int compare(Integer i1,Integer i2){
            return i2-i1; //降序排列
        }
    });

  PriorityQueue的經常使用方法有:poll(),offer(Object),size(),peek()等。

  2.平均值應該定義爲double,且(a+b)/2.0 。

  3.往最大堆中插入數據時間複雜度是O(logn),獲取最大數的時間複雜度是O(1)。

  4.這道題關鍵在於分紅兩個平均分配的部分,奇偶時分別插入到最大最小堆中,利用最大最小堆性質的插入方法要掌握。

 

更多:《劍指Offer》Java實現合集 

相關文章
相關標籤/搜索