原题
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
解读,
有一个会不断增长的数据,一直在添加新的数。需要求的每次新加数后,求整体的中位数(从第一个数开始到新的数)
例如
输入 1
中位数 1
再输入 2
中位数 1.5
再输入 3
中位数 2
再输入 0
中位数 0.5
class Solution {
/*
后来的数会和前面的数比较,来确定位置,所以需要保存全部的数
而中位数只需要最中间的数(一个或两个)
所以只需要确定中间的数的值,
直接排序所有数效率比较差,用堆排序速度更快。因为只需要知道前(后)一半中的最大(小)值,不是全部的顺序
这样,对每个新的值,判断其放在哪个堆(放在前半还是后半)
如果某个值放入后,两个部分差值大于1,也就说明某个部分不是只占一半了,那就拿走一个(堆顶)给另外一个堆,再重新取得堆顶
中位数中,偶数个取平均值,直接计算即可,奇数个时根据设定,哪个堆多一个,就取哪个堆的堆顶
直接设定某个堆天生可以比另外一个多1最好。
*/
priority_queue<int, vector<int>, less<int> > p;//大顶堆 队列,用来保存前半部分(小的数)
priority_queue<int, vector<int>, greater<int> > q;//小顶堆 队列,用来保存后半部分(大的数)
public:
//插入操作
void Insert(int num) {
if (p.empty())//堆为空,保存到可以多一的队列中
{
p.push(num);
}
else if (num > p.top())//如果新的数比 前半部分中最大值还大,那么就将其加入后半部分
{
q.push(num);
}
else {//否则加入前半部分
p.push(num);
}
if (p.size() == q.size() + 2)//如果连续多个(平衡后两个)数都加入了前半部分,那就取前半部分的堆顶,放入后半部分
{
q.push(p.top());
p.pop();
}
else if (p.size() == q.size() - 1)//如果连续多个数都加入了后半部分,那就取后半部分的堆顶,放入前半部分。这个例子中,设定前仅半部分可以比后半多一个,所以当后半多1个的时候就放入前半
{
p.push(q.top());
q.pop();
}
}
//获取当前平均值
double GetMedian() {
return p.size() == q.size() ? (p.top() + q.top()) / 2.0 : p.top();//根据总数显示,两半总数相等,那就代表总数时偶数。取两个堆顶平均值,否则哪个比另外一个多一取哪个堆顶。例子中设定前半部分可以多一个,所以直接取前半部分堆顶
}
};