题目描述
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
样例描述
示例:
addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2
思路
队顶堆 (大根堆 + 小根堆)
- 预先设定如果是奇数个,让左边大根堆个数比右边小根堆个数多一。中位数就左边堆的堆顶。偶数就是两个堆的堆顶元素求平均数。
- 对于当前元素t,如果小于左边堆顶元素x,就插入到左边堆,否则插入到右边堆。在插入过程中要保证,左边堆的数目最多比右边堆的数目多一。 需要调整的话就移动左边堆堆顶的元素。 因为它是最大的,同时能保证整体满足左边小于右边的这个性质。
代码
class MedianFinder {
//左边大根堆和右边小根堆
PriorityQueue<Integer> leftMax;
PriorityQueue<Integer> rightMin;
public MedianFinder() {
leftMax = new PriorityQueue<>((a, b) -> b - a);
rightMin = new PriorityQueue<>((a, b) -> a - b);
}
public void addNum(int num) {
//如果左边空,或者比左边小,就插入到左边
if (leftMax.isEmpty() || num <= leftMax.peek()) {
leftMax.offer(num);
//判断是否需要调整, 左边最多比右边多一
if (leftMax.size() > rightMin.size() + 1) {
//将左边堆顶元素插入到右边
rightMin.offer(leftMax.poll());
}
} else {
rightMin.offer(num);
//同样判断是否满足右边不大于左边
if (rightMin.size() > leftMax.size()) {
leftMax.offer(rightMin.poll());
}
}
}
public double findMedian() {
//如果左边堆数大于右边,说明是奇数 注意是double除以2.0
if (leftMax.size() > rightMin.size()) return leftMax.peek();
else return (leftMax.peek() + rightMin.peek()) / 2.0;
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/