题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
思路 https://blog.csdn.net/moses1213/article/details/51332959
STL---heap概述 https://blog.csdn.net/moses1213/article/details/51332599
代码https://www.cnblogs.com/easonliu/p/4441916.html
与https://www.nowcoder.com/profile/2156888/codeBookDetail?submissionId=13722304
作者用到的方法非常巧妙,注意到我们只需要位于中间的两个数就可以得出中位数,当元素个数为奇数个时可以看成这两个数一样。中位数在前半部分它是最大的,右半部分是最小的。我们只要始终知道前半部分数里的最大的那个数,后半部分里那个最小的数就可以了,其他的不用关心。这明显是堆的功用,前半部分用一个最大堆,后半部分建一个最小堆。中位数要保持两个堆的数目之差不超过1.为了实现平均分配,可以在数据的总数目是偶数时把新数据插入到最小堆中,否则插入最大堆中。
另一个问题是保持最大堆的所有数据都小于最小堆中的数据。如果插入一个数使得总数目和为偶数,根据之前的原则应该插入最小堆中,但是新插入的元素可能比最大堆中的元素小怎么办?这就违反了最小堆的数据一定大于最大堆得原则。实际上这里插入的元素应该是新元素和最大堆中所有元素的最大值,最大堆正好有返回一个最大值的作用,所以新插入的元素先插入最大堆,然后取出最大值再插入最小堆中。如果插入元素使得数据总数目和为奇数,处理方法类似。
class Solution {
private:
vector<int> min; //数组中的后一半元素组成一个最小化堆
vector<int> max; //数组中的前一半元素组成一个最大化堆
public:
void Insert(int num) {
if(((min.size()+max.size()) & 1) == 0) { //偶数数据的情况下,则在最小堆中插入元素
if(max.size() > 0 && num < max[0]) {
max.push_back(num);
push_heap(max.begin(), max.end(), less<int>());
num=max[0];
pop_heap(max.begin(), max.end(), less<int>());
max.pop_back();
}
min.push_back(num); //把前一半找到的最大值放到后一半中
push_heap(min.begin(), min.end(), greater<int>());
} else {
if(min.size() > 0 && num > min[0]) { //奇数数据的情况下,则在最大堆中插入元素
min.push_back(num);
push_heap(min.begin(), min.end(), greater<int>());
num=min[0];
pop_heap(min.begin(), min.end(), greater<int>());
min.pop_back();
}
max.push_back(num); //把后一半找到的最大值放到前一半中
push_heap(max.begin(), max.end(), less<int>());
}
}
double GetMedian() {
int size=min.size() + max.size();
if(size==0) return -1;
double median = 0;
if((size&1) != 0) {
median = (double) min[0];
} else {
median = (double) (max[0] + min[0]) / 2;
}
return median;
}
};
更简洁的写法:
class Solution {
priority_queue<int, vector<int>, less<int> > p;
priority_queue<int, vector<int>, greater<int> > q;
public:
void Insert(int num){
if(p.empty() || num <= p.top()) p.push(num);
else q.push(num);
if(p.size() == q.size() + 2) q.push(p.top()), p.pop();
if(p.size() + 1 == q.size()) p.push(q.top()), q.pop();
}
double GetMedian(){
return p.size() == q.size() ? (p.top() + q.top()) / 2.0 : p.top();
}
};