描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
示例1
输入:
[5,2,3,4,1,6,7,0,8]
返回值:
"5.00 3.50 3.00 3.50 3.00 3.50 4.00 3.50 4.00 "
说明
数据流里面不断吐出的是5,2,3…,则得到的平均数分别为5,(5+2)/2,3…
代码
import java.util.ArrayList;
import java.util.Comparator;
public class Solution {
ArrayList<Integer> stream = new ArrayList<>();
public void Insert(Integer num) {
//[5,2,3,4,1,6,7,0,8]
stream.add(num);
stream.sort(Comparator.comparingInt(value -> value));
}
public Double GetMedian() {
if(stream.size() == 0){
return 0.0;
}
double result = 0;
if(stream.size() % 2 == 0){
result = (double) (stream.get(stream.size() / 2 -1) + stream.get(stream.size() / 2)) / 2;
} else {
result = stream.get(stream.size() / 2);
}
return result;
}
}
升级版代码
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(
new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2){
return o2 - o1;
}
}
);
int count = 0;
public void Insert(Integer num) {
//[5,2,3,4,1,6,7,0,8]
if(count % 2 == 0){
maxHeap.offer(num);
minHeap.offer(maxHeap.poll());
}else{
minHeap.offer(num);
maxHeap.offer(minHeap.poll());
}
count++;
}
public Double GetMedian() {
if(count == 0){
return 0.0;
}
if(count % 2 == 0){
return (minHeap.peek() + maxHeap.peek())/2.0;
}else{
return (double) minHeap.peek();
}
}
}
说明
// 初始化一个大根堆,存中位数左边的数据,一个小根堆,存中位数右边的数据
// 动态维护两个数据结构的大小,即最多只相差一个
// 因为要求的是中位数,那么这两个堆,大顶堆用来存较小的数,从大到小排列;
// 小顶堆存较大的数,从小到大的顺序排序*,显然中位数就是大顶堆的根节点与小顶堆的根节点和的平均数。
// 保证:小顶堆中的元素都大于等于大顶堆中的元素,所以每次塞值,并不是直接塞进去,而是从另一个堆中poll出一个最大(最小)的塞值
// 当数目为偶数的时候,将这个值插入大顶堆中,再将大顶堆中根节点(即最大值)插入到小顶堆中;
// 当数目为奇数的时候,将这个值插入小顶堆中,再讲小顶堆中根节点(即最小值)插入到大顶堆中;
// 取中位数的时候,如果当前个数为偶数,显然是取小顶堆和大顶堆根结点的平均值;如果当前个数为奇数,显然是取小顶堆的根节点