《剑指offer》刷题——【时间效率】面试题41:数据流中的中位数(java实现)
一、题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶
数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
二、题目分析
1. 数据结构选取
(1)数组
- 若数组未排序,采用Partition函数找到数组中中位数,插入O(1) 查找O(n)
- 若数组排序,插入O(n) 查找O(1)
(2)排序的链表
- 插入O(n)
- 定义两个指针指向链表中间节点,查找O(1)
(3)二叉搜索树
- 插入平均时间复杂度O(logn)
- 当树极度不平衡,插入O(n)
- 为得到中位数,在二叉树节点中添加一个表示子树节点数目的字段,使得在平均O(logn)内得到中位数,最差仍O(n)
(4)平衡二叉搜索树(AVL)
- AVL的平衡因子是左、右子树的高度差,把它改为左右子树节点数目之差
- 插入O(logn),查找O(1)
- 大部分编程语言未实现AVL树,短时间内实现不易
(5)最大堆、最小堆
- 将数据分为两部分,左边的数据都比右边的数据小
- 根据左边的最大数和右边的最小数,就可以确定中位数
- 左边部分用最大堆,右边部分用最小堆
- 堆插入O(logn),查找中位数O(1)
- 实现细节:
- 两个堆中数目相差不能超过1,保证中位数只会出现在堆交接处
- 大顶堆的所有数据都小于小顶堆,这样就满足了排序要求
三、代码实现
- Java的PriorityQueue 是从JDK1.5开始提供的新的数据结构接口,默认内部是自然排序,结果为小顶堆,也可以自定义排序器,比如下面反转比较,完成大顶堆。
import java.util.*;
public class Solution {
PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>();
PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(20,new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2){
return o2-o1;
}
});
private int count = 0;
public void Insert(Integer num) {
count++;
if(count%2==0){
if(!maxHeap.isEmpty() && num<maxHeap.peek()){
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(maxHeap.size()==minHeap.size()){
return (maxHeap.peek()+minHeap.peek())/2.0;
}
else if(maxHeap.size()>minHeap.size()){
return maxHeap.peek()*1.0;
}
else{
return minHeap.peek()*1.0;
}
}
}