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