数据流中的中位数

 

吐出的较小的N/2 个,都在大根堆里,较大的 N/2 个,都在小根堆里。java

此时 五、4,都在大根堆,小根堆没有数。api

此时应该从大根堆的堆顶弹出来,扔到小根堆里。数组

好比:先把 5 拿出来,再把堆最后一个元素 4 ,放在 5 的位置上。数组长度是 1000,可是如今的 heapsize 是 0~1 。code

因此大根堆里只有 4 ,小根堆里是 5blog

把堆顶的数字给弹出来。队列

先让 6 与 1 交换位置,而后把 0~ 4 改为 0~3 ,it

堆顶变小,而后经历一次向下沉的过程。减堆操做。io

1. 堆顶与最后一个元素交换class

2. 标记越界的 heapsize 减一im

3. 而后从 0 位置开始经历一个 heapify

又恢复成大根堆了。

 

为啥是最后一个数和他换,由于原本交换以后 6 是要失效的,heapsize 要减一,因此最好跟最后一个数交换。

两个堆之间差值 > 1 ,就从较大的一个堆里,弹出一个数到,另外一个堆里。

大根堆的堆顶和小根堆的堆顶,必定能搞出中位数来。

大根堆中收集的是较小的 N/2 个的最大值。

小根堆中收集的是较大的 N/2 个的最小值。

来了一个6,比大根堆的 4 大,到小根堆里去。

当前数不是 <= 大根堆的数,就扔到小根堆里面去。

当前数 <= 大根堆的数,就扔到大根堆里面去。

此时要把小根堆中的堆顶扔到 大根堆中去。

策略是固定的,当前数 <= 大根堆堆顶最大值,就扔到大根堆里去。不平衡了就从大根堆里把堆顶拿到小根堆里去。若是小根堆更大,差值 > 2 ,就把小根堆的堆顶,扔到大根堆里去。

时刻保持着较大的 N/2 在小根堆里。

时刻保持着较小的 N/2 在大根堆里。

这样的话,就能够随时拿到中位数了

优先级队列,就是堆。

由于每一个数它调整的代价只跟层数有关,因此每次增长仍是弹出来,都是O(logN)的。

大根堆增长或者减小一个数,只须要承担一个 log(N)的代价。

logN 你想象一下,40 亿与 32 。

只须要调整高度有关的数就好了,跟其余数没有关系的。

 

 

/* 大顶堆,存储左半边元素 */
private PriorityQueue<Integer> left = new PriorityQueue<>((o1, o2) -> o2 - o1);
/* 小顶堆,存储右半边元素,而且右半边元素都大于左半边 */
private PriorityQueue<Integer> right = new PriorityQueue<>();
/* 当前数据流读入的元素个数 */
private int N = 0;

public void Insert(Integer val) {
    /* 插入要保证两个堆存于平衡状态 */
    if (N % 2 == 0) {
        /* N 为偶数的状况下插入到右半边。
         * 由于右半边元素都要大于左半边,可是新插入的元素不必定比左半边元素来的大,
         * 所以须要先将元素插入左半边,而后利用左半边为大顶堆的特色,取出堆顶元素即为最大元素,此时插入右半边 */
        left.add(val);
        right.add(left.poll());
    } else {
        right.add(val);
        left.add(right.poll());
    }
    N++;
}

public Double GetMedian() {
    if (N % 2 == 0)
        return (left.peek() + right.peek()) / 2.0;
    else
        return (double) right.peek();
}
相关文章
相关标签/搜索