[LeetCode] 295. 数据流的中位数
题目描述;
中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。
- 例如
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
题目链接:
. - 力扣(LeetCode)
解题主要思路:
如果直接硬解的话,即用vector<int>来存储,用快排来排序(nlogn)的话,时间复杂度肯定会超了的。
如果我们在插入vector<int>时像玩卡牌一样使用插入排序(n)的话,大概率也是会超时,可能addnum会调用5*104次;
因此,我们可以使用两个堆(logn),第一个堆采用大堆的方式,存的是数组左边的数,第二个堆采用小堆的方式,存的是数组右边的数。再插入的时候,严格控制left的大小要么和right的大小相同,要么比right的大小多一,这样我们就能在高效插入数据的同时,也能以O(1)的时间拿到中位数。
解题代码:
class MedianFinder {
// 左边建大根堆
priority_queue<int, vector<int>> left;
// 右边建小根堆
priority_queue<int, vector<int>, greater<int>> right;
public:
MedianFinder() {
}
void addNum(int num) {
// left_size == right_size
if (left.size() == right.size()) {
if (left.empty() || num <= left.top()) {
left.push(num);
}
else { // num > left.top()
// 先放入right中,再将最right.top()放入左侧
right.push(num);
left.push(right.top());
right.pop();
}
}
else { // left_size + 1 == right_size
if (num <= left.top()) {
// 先放入左侧,再将left.top()放入右侧
left.push(num);
right.push(left.top());
left.pop();
}
else {
// num > left.top(); 直接放入right中
right.push(num);
}
}
}
double findMedian() {
if (left.size() == right.size()) return (left.top() + right.top())/2.0;
else return left.top();
}
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/