吐出的较小的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(); }