题目:
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
思路:
1. 各种方法的复杂度对比:
(1) 数组: 插入新数据并排序(O(n)),取中位数(O(1));
(2) 链表:插入新数据到正确顺序位置(O(n)),得到中位数(O(1));
(3) 二叉搜索树:插入新数据(平均(O(logn), 最差O(n)),得到中位数(平均(O(logn), 最差O(n));
(4) AVL树(平衡的二叉搜索树):插入新数据(O(logn)), 得到中位数(O(1)),但插入算法难实现;
(5) 最大堆和最小堆:插入新数据(O(logn)), 得到中位数(O(1))
使用最大堆保存小一半的值,最大值在堆顶;使用最小堆保存大一半的值,最小值在堆顶;
插入的过程中尽量保持两个堆中元素均衡,当总数为偶数时,新插入的值放到最小堆中(如果新值比最大堆的堆顶小,则将其插入最大堆,取出堆顶元素插入最小堆);当总数为奇数时,新插入的值放到最大堆中(如果新值比最小堆的堆顶大,则将其插入最小堆,取出堆顶元素插入最大堆)
取中位数时,如果总数为奇数,则取最小堆的堆顶为中位数;反之,取最大堆堆顶和最小堆堆顶的平均值为中位数;
插入的复杂度为O(logn),取中位数的复杂度为O(1)
代码如下:
class Solution {
public:
vector<int> min;
vector<int> max;
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>()); // 将堆max通过less规则重排,使插入的新值到相应位置
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)
throw exception();
double result = 0.;
if((size & 1) == 0)
result = ((double)(min[0] + max[0])) / 2.0;
else
result = min[0];
return result;
}
};