题目:
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
思路:
用大顶堆实现左边的的数据容器,小顶堆实现右边的数据容器。
有一点需要注意,我们要保证数据被平均分配到两个堆中,因此两堆大小之差不能超过1。实现方法是数据流中奇数位的数插入小顶堆中,偶数位的数插入大顶堆中。
同时还要保证大顶堆中的所有数都要小于小顶堆中的数。考虑一种特殊情况,例如,插入偶数位的数,按照分配规则应该插入大顶堆中,但是这个数大于小顶堆的最小值怎么办?如果直接插入大顶堆,就不符合大顶堆所有值都小于小顶堆的值这一条件。可以这样解决:想把该数插入小顶堆,然后进行堆排序,接着讲小顶堆的最小值拿出来插入到大顶堆中,这样就满足所有条件了。同理插入奇数位的数时也要考虑这种特殊情况。
往堆中插入一个数据时间复杂度为O(logn),而得到最大值(或最小值)只需O(1)时间。
class Solution {
public:
void Insert(int num)
{
// 如果已有数据为偶数,则放入最小堆
if(((max.size() + min.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 0;
}
// 如果数据为偶数
if((size & 1) == 0){
return (min[0] + max[0]) / 2.0;
}
// 奇数
else{
return min[0];
}
}
private:
// 使用vector建立最大堆和最小堆,min是最小堆数组,max是最大堆数组
vector<int> min;
vector<int> max;
};