中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,
[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
进阶:
如果数据流中所有整数都在 0 到 100 范围内,你将如何优化你的算法?
如果数据流中 99% 的整数都在 0 到 100 范围内,你将如何优化你的算法?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-median-from-data-stream
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题报告:
对顶堆的完美应用。
注意点1:在对顶堆启动阶段的时候,只需要判断其中一个堆是否为空就好了,不需要两个都有值。
注意点2:把push元素和堆大小的维护给拆成两个原子操作,便于理解,也便于每一部分代码做唯一的事情。
AC代码:
class MedianFinder {
public:priority_queue<int> q1;//大根堆priority_queue<int, vector<int>, greater<int>> q2;//小根堆MedianFinder() { }void addNum(int num) {if(q1.empty()) q1.push(num);// else if(q2.empty() && num > q1.top()) q2.push(num);else if(num < q1.top()) q1.push(num);else q2.push(num);if((int)q1.size() - (int)q2.size() > 1) {int x = q1.top();q1.pop();q2.push(x);} else if((int)q2.size() - (int)q1.size() > 1) {int x = q2.top();q2.pop();q1.push(x);}}double findMedian() {if(q1.size()+q2.size() == 0) return 0;if(q1.size() == 0) return q2.top();if(q2.size() == 0) return q1.top();if(q1.size() < q2.size()) return q2.top();else if(q1.size() > q2.size()) return q1.top();else return (q1.top()+q2.top())/2.0;}
};/*** Your MedianFinder object will be instantiated and called as such:* MedianFinder* obj = new MedianFinder();* obj->addNum(num);* double param_2 = obj->findMedian();*/
注意点3:可以人为的做一些约束,限制两个pq的灵活性,来缩短代码量。比如强制要求小根堆的元素多于或等于大跟堆的。
0<=q2.size-q1.size()<=1。
这样实现只需要:
double findMedian() {if (queMin.size() > queMax.size()) {return queMin.top();}return (queMin.top() + queMax.top()) / 2.0;}