设计一个数据结构,可动态地维护一组数据,且支持如下操作:
(1)添加元素:void addNum(int num)
(2)返回这组数据中的中位数 double findMedian()
【思考】如何获取一组元素的中位数
(1)首先,我们马上想到的方法,最直观的方法就是:添加元素的同时进行排序操作(直插sort)addNum的复杂度是O(n),findMedian的复杂度则是O(1);
(2)我们也可以考虑在查询中位数的时候进行排序,这样addNum的复杂度是O(1),而findMedian的复杂度则是O(nlogn)。然而,如果addNum和findMedian的操作都是随机操作,共进行n次,则整体的时间复杂度最佳为O(n2)
这样的复杂度很明显不能满足要求,因此,我们需要试图寻找最佳的解决策略,这里我们巧妙地利用了堆的性质:
这里,我们动态地维护了一个大顶堆 max_heap 和一个小顶堆 min_heap,这两个堆各自存放“一半”的数据(这里的“一半”是指两个堆的size()相差小于等于1),且维持 max_heap 的堆顶 <= min_heap 的堆顶。如何实现呢?我们这样设计:
在插入新元素x时,如果max_heap为空,直接将x压入max_heap;
否则,先比较 max_heap.size() 与 min_heap.size():
若max_heap与min_heap元素个数相同:
若 x < max_heap 的堆顶,将x压入 max_heap;
否则,将x压入 min_heap;
若 max_heap 比 min_heap 的size小(需要通过调整两个堆,以保证平衡):
若 x < max_heap 的堆顶,直接将x压入 max_heap;
否则,将 min_heap 的堆顶弹出并压入 max_heap,再将x压入min_heap
若 max_heap 比 min_heap 的size大(同样需要通过调整两个堆):
若 x > max_heap 的堆顶,直接将x压入 min_heap;
否则,将 max_heap 的堆顶弹出并压入 min_heap,再将x压入max_heap
至此addNum()的流程已经完成,接下来需要考虑如何求中位数了。
若max_heap与min_heap元素个数相同:
median = max_heap 的堆顶与 min_heap 的堆顶的算数平均值
若 max_heap 比 min_heap 的size小:
median = min_heap 的堆顶
否则:
median = max_heap 的堆顶
代码如下:
class MedianFinder {
public:
void addNum(int num);
double find_median();
private:
std::priority queue<int, std::vector<int>, std::less<int>> max_heap;
std::priority queue<int, std::vector<int>, std::greater<int>> min_heap;
};
void MedianFinder::addNum(int num) {
if (max_heap.empty()) {
max_heap.push(num);
return;
}
if (max_heap.size() == min_heap.size()) {
if (num < max_heap.top()) {
max_heap.push(num);
} else {
min_heap.push(num);
}
} else if (max_heap.size() < min_heap.size()) {
if (num < max_heap.top()) {
max_heap.push(num);
} else {
max_heap.push(min_heap.top());
min_heap.pop();
min_heap.push(num);
}
} else {
if (num < max_heap.top()) {
min_heap.push(max_heap.top());
max_heap.pop();
max_heap.push(num);
} else {
min_heap.push(num);
}
}
}
int MedianFinder::find_median() {
if (max_heap.size() == min_heap.size()) {
return (max_heap.top() + min_heap.top()) / 2.0;
} else if (max_heap.size() < min_heap.size()) {
return min_heap.top();
}
return max_heap.top();
}