如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
解题思路:
数据流证明是输入数据大小是动态变化的。
我们要从动态变化的数据流中找现有数据的中位数。首先需要考虑,这些数据流中的数据该怎么存储?
即 用什么数据结构进行存储? 从而最方便查找中位数。
用数组来存储,如果是无序数组,则插入效率O(1) , 查找中位数效率O(n);
如果用有序数组来存储,那么插入O(n),查找中位数效率O(1);
如果用链表来存储,插入O(1),查找O(n);
用二叉搜索树来存储,插入平均O(logn),最差O(n),查找中位数效率平均O(logn),最差O(n);
AVL树,插入O(logn),查找中位数O(1);
最大堆 和 最小堆,插入O(logn),查找中位数O(1).
AVL树在C++STL中没有直接库函数可用,需要自行编写,实现起来比较复杂。
因此,用堆来实现本题目的需求是最佳选择。
利用C++,STL库中,push_heap()、pop_heap()、vector 以及 比较函数 less<type>() 和 greater<type>()
来实现堆得操作。
具体思路如下:
1、维护两个堆,一个小顶堆,一个大顶堆。
大顶堆max | 小顶堆min |
max中存储当前数据流中较小的一半;
min中存储当前数据流中较大的一半;
同时,需要满足:max中的所有数据 都 小于 min中存储的所有数据。
(这里要在编程实现的时候做处理,即拿当前的数据与堆顶数据进行比较。然后,拿到符合要求的数据)
这样,数据流中的数据分为两个部分,较大的一部分、较小的一部分。
取中位数就直接取大顶堆堆顶 或 小顶堆堆顶。
当数据流中数据数目是奇数个就插入——>小顶堆;
当数据流中数据数目是偶数个就插入——>大顶堆;
那么,最后,取中位数,奇数就去小顶堆,偶数就取两个堆顶和的平均值。
实现代码:
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 = max.size() + min.size();
if(size == 0)
{
return 0.0;
}
double median = 0.0;
if((size & 1) == 1)
{
median = static_cast<double>(min[0]);
}
else
{
median = static_cast<double>((min[0] + max[0])/2.0);
}
return median;
}
private:
vector<int> max;
vector<int> min;
};