1,题目要求
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
2,题目思路
对于这道题,要去在时刻插入数据的同时,返回当前数组中的中位数。
在计算中位数时,有两种情况,一种是数组长度为奇数时,返回当前数组的中间值——nums[nums.size()/2]即可。
当数组长度为偶数时,则是靠近中间对称的两个数字的平均值。
因此,如果我们在构建数组的过程中,能够时刻对当期数组的中间元素进行记录,就可以时刻获得当前数组的中位数了。
事实上,如果我们在构建数组时,将数组一分为二,使得left中的数组值都比right中的数组值要小,而且可以从left中取到其中最大的,而且可以从right中取到其最小的,这样,就可以分轻松的实现这个问题了。
在解决上,我们使用优先队列——priority_queue,即一种堆结构,其中:
- priority_queue<int, vector, less> left,构建一个大顶堆
- priority_queue<int, vector, greater> right,构建一个小顶堆
left中,top元素即为当前左半部分最大的;
right中,top元素即为当前右半部分最小的;
于是,进行Insert时;除了要保证左半部分的值都比右半部分的值小之外,还需要保持,左边大顶堆的大小最多只能比右边小顶堆的大小大1(由于奇数次的插入)
又因为最后返回的,要么是左侧的top、要么是左侧top与右侧top的平均值,因此,右侧的小顶堆的大小不能比左边的大顶堆的大小要大。
3,代码实现
class Solution {
public:
void Insert(int num)
{
if(left.empty() || num <=left.top())
left.push(num);
else
right.push(num);
if(left.size() > right.size()+1){
right.push(left.top());
left.pop();
}
if(left.size() < right.size()){
left.push(right.top());
right.pop();
}
return;
}
double GetMedian()
{
if(left.size() == right.size())
return (left.top()+right.top())/2.0;
else
return left.top();
}
private:
priority_queue<int, vector<int>, less<int>> left;
priority_queue<int, vector<int>, greater<int>> right;
};