求資料流中的中位數問題
作者:Grey
原文地址: 求資料流中的中位數問題
題目連結
LeetCode 295. Find Median from Data Stream
主要思路
要得到資料流中的中位數,在偶數的情況下,要得到上下中位數求平均,在奇數的狀態下,要得到中間位置的數,這裡最關鍵的問題就是:如何快速找到中間位置(而且是動態的)。
我們可以準備兩個堆,一個大根堆,一個小根堆,兩個堆分別維持在一半左右的元素,且讓這兩個堆的堆頂始終保持中間位置的元素。這樣就可以快速得到中位數需要的值了。具體操作如下:
第一個元素永遠是先進大根堆。
接下來的元素,按如下規則進入:
如果接下來的元素比大根堆堆頂元素小,進大根堆,否則進入小根堆。
如果兩個堆的大小差值已經達到2了,說明元素要向著一側堆傾斜,這個時候,為了維持兩個堆的平衡(即:始終可以拿到中位數需要的資訊),從數量多的堆拿出一個放到數量少的堆,這樣就讓兩個堆始終保持差值小於等於1。
此時,如果要得到中位數,通過如下規則就可以得到:
如果大根堆和小根堆數量一樣,說明原始資料流是偶數個,那麼直接拿出大根堆和小根堆的堆頂元素求和再除以2就是了。
如果大根堆和小根堆數量不一樣(在這一步,只能是差1),那麼就取多的那個堆的堆頂即為中位數。
完整程式碼如下
import java.util.Comparator;
import java.util.PriorityQueue;
class MedianFinder {
private final PriorityQueue<Integer> minHeap;
private final PriorityQueue<Integer> maxHeap;
public MedianFinder() {
minHeap = new PriorityQueue<>(Comparator.comparingInt((Integer o) -> o));
maxHeap = new PriorityQueue<>((o1, o2) -> o2 - o1);
}
public void addNum(int num) {
if (maxHeap.isEmpty()) {
maxHeap.add(num);
} else {
if (maxHeap.peek() >= num) {
maxHeap.add(num);
} else {
minHeap.add(num);
}
}
int maxHeapSize = maxHeap.size();
int minHeapSize = minHeap.size();
if (maxHeapSize - minHeapSize == 2) {
minHeap.add(maxHeap.poll());
} else if (minHeapSize - maxHeapSize == 2) {
maxHeap.add(minHeap.poll());
}
}
public double findMedian() {
if (maxHeap.size() == minHeap.size()) {
return (maxHeap.peek() + minHeap.peek()) / 2d;
}
if (maxHeap.size() > minHeap.size()) {
return maxHeap.peek();
}
return minHeap.peek();
}
}