题面
中位数 是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。
- 例如
arr = [2,3,4]的中位数是3。 - 例如
arr = [2,3]的中位数是(2 + 3) / 2 = 2.5。
实现 MedianFinder 类:
-
MedianFinder()初始化MedianFinder对象。 -
void addNum(int num)将数据流中的整数num添加到数据结构中。 -
double findMedian()返回到目前为止所有元素的中位数。与实际答案相差10-5以内的答案将被接受。
示例 1:
输入 ["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"] [[], [1], [2], [], [3], []] 输出 [null, null, null, 1.5, null, 2.0]
解释 MedianFinder medianFinder = new MedianFinder(); medianFinder.addNum(1); // arr = [1] medianFinder.addNum(2); // arr = [1, 2] medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2) medianFinder.addNum(3); // arr[1, 2, 3] medianFinder.findMedian(); // return 2.0
提示:
-105 <= num <= 105- 在调用
findMedian之前,数据结构中至少有一个元素 最多5 104次调用addNum和findMedian
分析
-
可以使用两个堆来维护,其中大顶堆维护中位数左侧的元素,小顶堆维护右侧的元素
-
当要加入一个新元素时,要尽量维护两个堆的大小尽量相等,其中大顶堆比小顶堆可以多一个元素
-
如果当前
left的大小和right的大小相等:- 如果添加的数字
num比较大,比如添加 7,那么把 7 加到right中。现在left比right少 1 个数,不符合前文的规定,所以必须把right的最小值从right中去掉,添加到left中。如此操作后,可以保证left的所有元素都小于等于right的所有元素。 - 如果添加的数字
num比较小,比如添加 0,那么把 0 加到left中。 - 这两种情况可以合并:无论
num是大是小,都可以先把num加到right中,然后把right的最小值从right中去掉,并添加到left中。
- 如果添加的数字
-
如果当前
left比right多 1 个数:- 如果添加的数字
num比较大,比如添加 7,那么把 7 加到right中。 - 如果添加的数字
num比较小,比如添加 0,那么把 0 加到left中。现在left比right多 2 个数,不符合前文的规定,所以必须把left的最大值从left中去掉,添加到right中。如此操作后,可以保证left的所有元素都小于等于right的所有元素。 - 这两种情况可以合并:无论
num是大是小,都可以先把num加到left中,然后把left的最大值从left中去掉,并添加到right中。
- 如果添加的数字
-
代码实现
class MedianFinder { priority_queue<int> left; // 最大堆 priority_queue<int, vector<int>, greater<>> right; // 最小堆 public: void addNum(int num) { if (left.size() == right.size()) { right.push(num); left.push(right.top()); right.pop(); } else { left.push(num); right.push(left.top()); left.pop(); } } double findMedian() { if (left.size() > right.size()) { return left.top(); } return (left.top() + right.top()) / 2.0; } };