前言
本题是我对写题的思路总结
一、题目解析
易知,本题是通过实现一个MedianFinder 类,来解决有序数组的中位数问题。
二、解题思路
1. 暴力求解
我们非常容易想到,用vector数组存储数据,在每次调用addNum函数时,先加入数组中,再对数组排序。那求解有序数组的中位数,只要判断数组元素数量是奇数还是偶数,就可以求出中位数了。但这样addNum函数的时间复杂度就是 O( nlog^n)。
2. 插入排序
该思路与暴力求解类似,只不过把sort排序,换成插入排序。我们同样使用vector num数组存储数据,只不过每次调用addNum函数时,我们直接从数组尾部向数组头部遍历数组,当遍历到一个数组元素小于要插入元素时,将元素插入该位置。这样addnum函数的时间复杂度为O(n)。
3. 大小堆求解
该方法我们将有序数组分为两个堆,其中前一部分为大堆,后一部分为小堆。
那么此时,我们易知,当left.size() == right.size()时,中位数mid = ( left.top() + right.top() ) / 2.0;当left.size() == right.size() + 1时,中位数mid = left.top()。
因为此时left.top() 不就是前一部分的最后一个元素,right.top()不就是后一部分的第一个元素。
那么如果我们要使上面方法成立,那我们必须满足 m == n || m == n + 1;
那么此时,对于调用addNum函数,我们有一个问题,那就是对于该插入的元素,我们要插入到 left 还是 right?
我们先定义如下变量:
我们分为两个情况考虑,a. m == n 。 b. m == n + 1。
- 上述成立与否,是为了满足上面求中位值的方法。
- 通过比较 x 与 val 的大小关系,来判断插入位置,是因为 x 是前一部分的最大值,如果 x < val 就表示 val 是属于 x 的右边部分(在有序数组中的位置)
此时我们就可以使用大小堆来求解中位数的问题了。
三、代码实现
使用大小堆的思路求解
class MedianFinder {
public:
MedianFinder() {}
void addNum(int num) {
int m = left.size(), n = right.size();
if(m == n)
{
if(left.size() == 0 || left.top() >= num)
{
left.push(num);
}
else if(left.top() < num)
{
right.push(num);
left.push(right.top());
right.pop();
}
}
else if(m == (n + 1))
{
if(num <= left.top())
{
left.push(num);
right.push(left.top());
left.pop();
}
else if(num > left.top())
{
right.push(num);
}
}
}
double findMedian() {
int m = left.size(), n = right.size();
if(m == n)
return (left.top() + right.top()) / 2.0;
return left.top() * 1.0;
}
private:
priority_queue<int, vector<int>, less<int>> left;
priority_queue<int, vector<int>, greater<int>> right;
};
总结
以上就是我对于如何使用优先级队列来处理数据流的中位数。