题目描述: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是右边最小的数,即便左右两部分数据不是有序的,咱们也有一个结论就是:左边最大的数小于右边最小的数。排序
<div align=center>  </div>   所以,咱们能够有以下的思路:**用一个最大堆实现左边的数据存储,用一个最小堆实现右边的数据存储,**向堆中插入一个数据的时间是O(logn),而中位数就是堆顶的数据,只须要O(1)的时间就可获得。it
而在具体实现上,首先要保证数据平均分配到两个堆中,两个堆中的数据数目之差不超过1,为了实现平均分配,能够在数据的总数目是偶数时,将数据插入最小堆,不然插入最大堆。
此外,还要保证全部最大堆中的数据要小于最小堆中的数据。因此,新传入的数据要和最大堆中最大值或者最小堆中的最小值比较。当总数目是偶数时,咱们会插入最小堆,可是在这以前,咱们须要判断这个数据和最大堆中的最大值哪一个更大,若是最大值中的最大值比较大,那么将这个数据插入最大堆,并把最大堆中的最大值弹出插入最小堆。因为最终插入到最小堆的是原最大堆中最大的,因此保证了最小堆中全部的数据都大于最大堆中的数据。
总结:
<div align=center>  </div> <div align=center>  </div>
编程实现(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; } }