題目描述: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是右邊最小的數,即便左右兩部分數據不是有序的,咱們也有一個結論就是:左邊最大的數小於右邊最小的數。
排序
而在具體實現上,首先要保證數據平均分配到兩個堆中,兩個堆中的數據數目之差不超過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; } }