描述:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
数据范围:数据流中数个数满足 1≤n≤1000 ,大小满足 1≤val≤1000 。
进阶: 空间复杂度 O(n) , 时间复杂度 O(nlogn) 。
输入:[5,2,3,4,1,6,7,0,8]
返回值:"5.00 3.50 3.00 3.50 3.00 3.50 4.00 3.50 4.00 "
说明:数据流里面不断吐出的是5,2,3...,则得到的平均数分别为5,(5+2)/2,3...
输入:[1,1,1]
返回值:"1.00 1.00 1.00 "
数据流中的中位数:最直观的想法是,使用sort函数对数组进行排序,如果数组长度为奇数,则返回数组最中间的一个数,反之如果数组长度为偶数,则返回数组最中间的两个数之和的平均值。
vector<int> res; //结果数组
void Insert(int num)
{
//存储数据
res.push_back(num);
}
double GetMedian()
{
//排序
sort(res.begin(),res.end());
//求长度
int n=res.size();
//中位数结果
double result;
//数组长度为奇数
if(n%2!=0)
result=res[n/2];
//数组长度为偶数
else
result=(res[n/2]+res[n/2-1])/2.0;
return result;
}
优化:排序是从小到大的数组,例如[0,1,2…medium-1,medium,medium+1…n-2,n-1,n],将其分为前半部分[0,1,2…medium-1]和后半部分[medium+1…n-2,n-1,n],此时我们可以使用对顶堆来进行存储,即使用大顶堆存储前半部分,使用小顶堆存储后半部分,既然元素是从小到大,那么我们需要满足大顶堆的所有元素都要比小顶堆的所有元素小,且大顶堆与小顶堆相差的元素个数不能超过1,我们假设如果两个堆的元素个数不相等的话小顶堆的元素个数比大顶堆的元素个数多一,即假如元素为1,2,3,4,5,那么大根堆存储1,2,小根堆存储3,4,5。那么如何实现大顶堆的所有元素都要比小顶堆的所有元素小呢?我们在插入元素时,如果两个堆的元素个数不相等,则先将元素插入到小根堆,然后再弹出元素到大根堆,即使得小根堆元素个数与大根堆元素个数相等,反之如果两个堆的元素个数相等,则先将元素插入到大根堆,然后再弹出元素到小根堆,即使得小根堆元素个数比大根堆元素个数多一,同时也保持了两个堆的元素大小关系。最后,如果两个堆的元素个数不相等,则肯定是小根堆的元素个数比大根堆的元素个数多一,直接返回小根堆的堆顶即可,反之返回小根堆的堆顶和大根堆的堆顶的平均值。
priority_queue max_que; //大根堆
priority_queue,greater> min_que; //小根堆
void Insert(int num)
{
//两个堆的元素个数不相等
if(max_que.size()!=min_que.size())
{
min_que.push(num);
max_que.push(min_que.top());
min_que.pop();
}
//两个堆的元素个数相等
else
{
max_que.push(num);
min_que.push(max_que.top());
max_que.pop();
}
}
double GetMedian()
{
double result; //结果
//两个堆元素相等
if(min_que.size()==max_que.size())
result=(min_que.top()+max_que.top())/2.0;
else
result=min_que.top();
return result;
}