题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
首先在剑指offer上这个题目说的不是很清楚,两个函数的意思是,一个Insert函数代表插入一个数到序列中,而GetMedian函数表示返回一个中位数。
思路:
首先根据题目描述,可以试想将数据分为两个堆,一个最大堆一个最小堆,因为中位数的一个性质是比前面一半的元素都大比后面一半的元素都小,所以可以建两个堆,但是在STL中没有堆的数据结构而是用优先队列实现的priority_queue。默认情况下优先队列是最大堆,所以接下来处理是如果加入一个数序列具有奇数个,然后就放到最小堆,偶数个就放到最大堆,但是还有一个问题存在,如果直接放到最大堆,但是如果这个数比右边的最小堆中的某个数大,那么就违反了中位数的性质,所以可以先将这个数放到最小堆中,然后取最小值(时间复杂度O(1))放到最大堆中,那么就满足了中位数的性质,相当于一个调整的过程,反之亦然。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
priority_queue<int>big;//大根堆
priority_queue<int,vector<int>,greater<int> >small;//小根堆
void Insert(int num)
{
int s1 = big.size();
int s2 = small.size();
if((s1+s2)%2==0)
{
small.push(num);
big.push(small.top());
small.pop();
}
else
{
big.push(num);
small.push(big.top());
big.pop();
}
}
double GetMedian()
{
int s1 = small.size();
int s2 = big.size();
double ans;
if((s1+s2)%2==0) ans = (small.top()+big.top())*1.0/2;
else ans = big.top()*1.0;
return ans;
}
int main()
{
int a[] = {5,7,9,4,3,10};
for(int i=0;i<6;i++)
{
Insert(a[i]);
printf("%lf\n",GetMedian());
}
return 0;
}