面试题41:数据流中的中位数
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
因为是数据流,所以需要随时能得到目前已获得数据的中位数。
思路1:每次都进行排序。(这种方法在 leetcode 上会超出时间限制)
代码实现:
class MedianFinder {
private List<Double> list;
/** initialize your data structure here. */
public MedianFinder() {
list = new ArrayList<>();
}
public void addNum(double num) {
list.add(num);
}
public double findMedian() {
Collections.sort(list);
if(list.size() % 2 == 0) {
int len = list.size();
double num1 = list.get(len/2);
double num2 = list.get(len/2 - 1);
return (num1 + num2) / 2;
} else {
return list.get(list.size() / 2);
}
}
}
思路2:用优先队列来解决这个问题
1、优先队列1:大顶堆,存储数据中的较小数,存储 len/2 或 len/2+1 个数。
2、优先队列2:小顶堆,存储数据中的较大数,存储 len/2 个数。
3、每次数据先存入优先队列1,若优先队列1中的数据个数比优先队列2多两个,则出队一个元素加入优先队列2。若优先队列1的队头元素比优先队列2的队头元素大,则优先队列1出队一个元素到优先队列2,优先队列2出队一个元素到优先队列1。
4、输出时,若优先队列1的数据个数为 len/2+1,则返回优先队列1的队头元素。若优先队列1的元素个数为 len/2,则返回两个对头元素的平均值。
代码实现:
class MedianFinder {
private Queue<Double> priorityQueue1;
private Queue<Double> priorityQueue2;
/** initialize your data structure here. */
public MedianFinder() {
priorityQueue1 = new PriorityQueue<>(new Comparator<Double>() {
@Override
public int compare(Double o1, Double o2) {
if(o2 - o1 > 0) return 1;
else return -1;
}
});
priorityQueue2 = new PriorityQueue<>();
}
public void addNum(double num) {
priorityQueue1.add(num);
if(priorityQueue1.size() > priorityQueue2.size() + 1) {
priorityQueue2.offer(priorityQueue1.poll());
}
if(priorityQueue2.size() != 0 && priorityQueue1.peek() > priorityQueue2.peek()) {
priorityQueue2.offer(priorityQueue1.poll());
priorityQueue1.offer(priorityQueue2.poll());
}
}
public double findMedian() {
if(priorityQueue1.size() == priorityQueue2.size()) return (priorityQueue1.peek() + priorityQueue2.peek()) / 2;
else return priorityQueue1.peek();
}
}