剑指OFFER题40------按牛客网通过率排序
时间:2019.1.4.2206
作者:Waitt
题目
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
时间限制:1秒 空间限制:32768K 热度指数:99031
解法
解法1
利vector保存数值,每次插入数值之后重新对向量进行排序,取出中间的数据
解法2
利用平衡二叉树保存数据,但是一般AVL树并无标准库实现,自己写插入和删除操作比较繁琐。
解法3
利用一个大顶堆和一个小顶堆存放数据,大顶堆的数据均比小顶堆小。大顶堆的容量<=小顶堆容量+1。
数据数目为奇数时,返回大顶堆的堆顶;
数据数目为偶数时,返回(大顶堆堆顶+小顶堆堆顶)/2.0;由于返回时数据类型可能为double,所以此处应除2.0
利用优先队列实现大顶堆和小顶堆:priority_queue
class Solution {
public:
priority_queue<int, vector<int> > a;//大顶堆
priority_queue<int, vector<int>, greater<int> > b;//小顶堆
int c=0;//计数
void Insert(int num)
{
c++;
a.push(num);//每次都先存入大顶堆中
if(c%2==0)//当为偶数时,将大顶堆的堆顶存入小顶堆
{
b.push(a.top());
a.pop();
}
if(c>2)//由于每次都是存入大顶堆,故总数为奇数时,需要查验大顶堆的堆顶是否比小顶堆的堆顶小
{
if(c%2!=0)//当为奇数时,进行查验,确保小顶堆的所有数据都比大顶堆的大
{
if(a.top()>b.top())
//当大顶堆的堆顶大于小顶堆的堆顶时,进行数据替换。确保小顶堆的所有数据都比大顶堆的大
{
b.push(a.top());
a.pop();
a.push(b.top());
b.pop();
}
}
}
}
double GetMedian()
{
if(c%2==0)
return (a.top()+b.top())/2.0;//为了确保返回的为double,此处应除以2.0
else
return a.top();
}
};
利用make_heap创建堆进行实现
由于此题是一个一个输入数据,因此无需用make_heap,用push_heap即可
class Solution {
public:
vector<int> a,b;//大顶堆a,小顶堆b
void Insert(int num)
{
if(a.empty()||num<a[0])//小数放入小顶堆
{
a.push_back(num);
//由于此题是一个一个输入数据,因此无需用make_heap,用push_heap即可
push_heap(a.begin(),a.end());
}
else//大数放入大顶堆
{
b.push_back(num);
push_heap(b.begin(),b.end(),greater<>());
}
if(a.size()>(b.size()+1))//数组容量判断
{
b.push_back(a[0]);
push_heap(b.begin(),b.end(),greater<>());
pop_heap(a.begin(),a.end());
a.pop_back();
}
if(b.size()>a.size())//数组容量判断
{
a.push_back(b[0]);
push_heap(a.begin(),a.end());
pop_heap(b.begin(),b.end(),greater<>());
b.pop_back();
}
}
double GetMedian()
{
if(a.size()>b.size())//说明此时为奇数个数
return a[0];
return (a[0]+b[0])/2.0;
}
};
TIPS
优先队列的本质是用堆实现的,其拥有队列的所有属性。
其默认顺序与堆一样:less<>为大顶堆。当需要小顶堆时:greater<>.
优先队列priority_queue:https://blog.csdn.net/AAMahone/article/details/82787184
优先队列priority_queue:https://blog.csdn.net/weixin_36888577/article/details/79937886