题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
题解
解题思路—直接插入:使用ArrayList,每次输入数据时,使用直接插入的思想,将数据按顺序插入list中。
但是对于海量数据而言,直接插入法效率太低,所以推荐使用大顶堆小顶堆来解这道题,将数据尽量平均的存入大顶堆和小顶堆中,堆顶即为中位数。
规定:0<=小顶堆数据个数-大顶堆数据个数<=1
// minheap:小顶堆;maxheap:大顶堆。
// top_minheap:小顶堆的堆顶元素;top_maxheap:大顶堆的堆顶元素。
1.当堆中数据总数为奇数时(小顶堆个数比大顶堆多一个)
当num<=top_minheap,将insert的数据num直接放入大顶堆;
当num>top_minheap,先将top_minheap加入maxheap,再将num加入minheap中。
此时,堆中数据个数变为偶数,中位数输出:(top_minheap+top_maxheap)/2
2.当堆中数据总数为偶数时(小顶堆大顶堆个数一致)
当num>=top_maxheap,将insert的数据num直接放入小顶堆;
当num<top_maxheap,先将top_maxheap加入minheap,再将num加入maxheap中。
此时,堆中数据个数变为奇数,最后中位数输出:top_minheap
代码:
import java.util.PriorityQueue;
public class Solution {
// 定义大顶堆,使用lambda表达式
public PriorityQueue<Integer> maxheap = new PriorityQueue<>((x,y)-> y-x);
// 定义小顶堆,PriorityQueue默认创建小顶堆
public PriorityQueue<Integer> minheap = new PriorityQueue<>();
// 是否为奇数
public boolean isOdd = false;
public void Insert(Integer num) {
if(!isOdd){ // 堆中数据偶数个
if(minheap.isEmpty())
minheap.add(num);
else{
if(num>=maxheap.peek())
minheap.add(num);
else{
minheap.add(maxheap.poll());
maxheap.add(num);
}
}
}else{ // 堆中数据奇数个
if(num<=minheap.peek())
maxheap.add(num);
else{
maxheap.add(minheap.poll());
minheap.add(num);
}
}
isOdd = !isOdd;
}
public Double GetMedian() {
if(minheap.isEmpty())
return 0.0;
if(isOdd)
return (double) minheap.peek();
else
return (double) (minheap.peek()+maxheap.peek())/2;
}
}
最小堆和最大堆解释
堆的存储
一般用数组来表示堆,若根结点存在序号0处, i结点的父结点下标就为(i-1)/2。i结点的左右子结点下标分别为2i+1和2i+2。
(注:如果根结点是从1开始,则左右孩子结点分别是2i和2i+1。)
如第0个结点左右子结点下标分别为1和2。
如最大化堆如下:
左图为其存储结构,右图为其逻辑结构。
参考链接
https://www.cnblogs.com/shengrang/p/3843487.html
https://blog.csdn.net/weixin_34206899/article/details/93166272