一个数据流中,怎么样做到随时可以取得中位数
-
题目描述:有一个源源不断地吐出整数的数据流,假设你有足够的空间来保存吐出的数。请设计一个名叫MedianHolder的结构,MedianHolder可以随时取得之前吐出所有树的中位数。
-
要求:
1.如果MedianHolder已经保存了吐出的N个数,那么任意时刻将一个新的数加入到MedianHolder的过程中,时间复杂度O(logN)。
2.取得已经吐出的N个数整体的中位数的过程,时间复杂度O(1).
减堆操作:
* //1.将堆顶位置上的数弹到小根堆
* 2.把最后一个叶子结点(即为堆的最好一个位置)的数放到(堆顶)根节点
* (或者说与堆顶的数交换,反正后面HeapSize都要减一的,堆顶的数最终都会失效,当然交换还是更容易操作)
* 3.将标记越界的变量(HeapSize)减一
* 4.从堆顶开始经历一个Heapify,即可恢复为大根堆
-
为啥一定要将最后一个位置的数放在堆顶? *
因为:标记越界的变量(HeapSize:表示当前一个堆已经形成的大小)需要减一,如果将其他位置上的 数 放在堆顶, *
势必会导致最后一个位置的数因为越界而失效如何将大根堆位置上的数赶到小根堆?
- 如果当前数大于大根堆中的堆顶的数,就直接扔到小根堆里面,否则扔到大根堆里面
- //1.将堆顶位置上的数弹到小根堆
- 2.把最后一个叶子结点(即为堆的最后一个位置)的数放到(堆顶)根节点
- (或者说与堆顶的数交换,反正后面HeapSize都要减一的,堆顶的数最终都会失效,当然交换还是更容易操作)
- 3.将标记越界的变量(HeapSize)减一
- 4.从堆顶开始经历一个Heapify,即可恢复为大根堆
- 如果大根堆与小根堆不平衡了(相差为2个数时),怎么办呢?
- 数较多的堆若为大根堆,就将大根堆中的最小的数(经过以上的排序后,最小的数已经到了大根堆堆顶)拿到小根堆中
- 数较多的堆若为小根堆,就将小根堆中的最大的数(经过以上的排序后,最大的数已经到了小根堆堆顶)拿到大根堆中,
- 使得大根堆与小根堆中分别盛放较小的N/2个元素和较大的N/2个元素
- 这样随时都可以随时拿到中位数
import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;
/**
* 随时找到数据流的中位数
* 思路:
* 利用一个大根堆和一个小根堆去保存数据,保证前一半的数放在大根堆,后一半的数放在小根堆
* 在添加数据的时候,不断地调整两个堆的大小,使得两个堆保持平衡
* 要取得的中位数就是两个堆堆顶的元素
*/
public class MedianQuick {
public static class MedianHolder {
private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(new MaxHeapComparator());
private PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>(new MinHeapComparator());
/**
* 调整堆的大小
* 当两个堆的大小差值变大时,从数据多的堆中弹出一个数据进入另一个堆中
*/
private void modifyTwoHeapsSize() {
if (this.maxHeap.size() == this.minHeap.size() + 2) {
this.minHeap.add(this.maxHeap.poll());
}
if (this.minHeap.size() == this.maxHeap.size() + 2) {