您的位置:首页 > 职场人生

【剑指Offer】面试题64:数据流中的中位数

2017-07-29 12:03 260 查看
整理自剑指Offer

牛客网https://www.nowcoder.com/questionTerminal/9be0172896bd43948f8a32fb954e1be1

一:题目描述

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

二:解题思路

由于数据从一个数据流中读出来的,数据的数目随着时间的变化而增加,如何选择一个容器存储数据,使插入一个数据后有序 与 计算中位数的时间复杂度性能最优?

如果利用两个指针去寻找中位数

N1N2...N2mN2m+1
奇数:P1与P2指向同一个位置

N1N2...N2m
偶数:P1,P2指向中间的两个位置(排序后)

整个容器被分成两部分,位于容器左部分的数据比右边的小

P1指向数据左部分最大的数

P2指向数据右边最小的数

如果能够保证数据容器左边的数据小于右边的数据,这样即使左右两边的数据没有排序,也可以根据左边最大的数及右边最小的数得到中位数。

如何快速从一个容器中找到最大的数?----大顶堆实现这个数据容器

如何快速从一个容器中找到最小的数?---小顶堆

要考虑的细节问题:

1.保证数据平均分配在两个堆中:因此两个堆中数目之差不能超过1

2.要保证大顶堆中所有的数据小于小顶堆中的数据

三:代码实现

大顶堆-小顶堆

class Solution {
public:

vector<int> max;//存放大顶堆
vector<int> min; //存放小顶堆

bool isInvalidInput=false;

//当前数据总数为偶数时,插入小顶堆,奇数时,插入大顶堆--保证大顶堆与小顶堆数据平衡
//插入大顶堆前,现将数据插入小顶堆,再将最小值插入到大顶堆中,保证大顶堆元素小于小顶堆元素
//同理,插入小顶堆的元素,现将数据插入到大顶堆,再将最大值插入到小顶堆中,保证小顶堆的元素大于大顶堆元素

void Insert(int num){
//偶数-插入小顶堆
if((min.size()+max.size())%2==0){

//将数据插入大顶堆,找到大顶堆中最大的元素
if(max.size()>0 && num<max[0]){
// push_heap (_First, _Last),要先在容器中加入数据,再调用push_heap ()
max.push_back(num);//先将元素压入容器
push_heap(max.begin(),max.end(),less<int>());//调整最大堆
num=max[0];//取出最大堆的最大值
//pop_heap(_First, _Last),要先调用pop_heap()再在容器中删除数据
pop_heap(max.begin(),max.end(),less<int>());//删除最大堆的最大值
max.pop_back(); //在容器中删除
}

//如果max为空 或者 num大于大顶堆最大值,则将元素直接插入小顶堆
min.push_back(num);//插入小顶堆
push_heap(min.begin(),min.end(),greater<int>());//调整小顶堆
}//if
else{
//奇数--插入大顶堆
if(min.size()>0 && num>min[0]){
// push_heap (_First, _Last),要先在容器中加入数据,再调用push_heap ()
min.push_back(num);//先压入小顶堆
push_heap(min.begin(),min.end(),greater<int>()); //调整小顶堆
num=min[0]; //获得小顶堆最小值
//pop_heap(_First, _Last),要先调用pop_heap()再在容器中删除数据
pop_heap(min.begin(),min.end(),greater<int>()); //删除小顶堆最小值
min.pop_back(); //从容器中删除
}
//如果小顶堆为空,或者num小于小顶堆最小值
max.push_back(num); //直接插入大顶堆
push_heap(max.begin(),max.end(),less<int>());//调整大顶堆
}
}
double GetMedian(){
int size=min.size()+max.size();
if(size<=0) //没有元素,抛出异常
{
isInvalidInput=true;
return 0; //throw exception("No numbers are available");
}

if(size%2==0)//偶数
return (max[0]+min[0])/2.0;
else
return min[0];
}

};

优先队列的方式实现

class Solution {
public:

priority_queue<int, vector<int>, less<int> > max; //优先队列,less保证队列中元素按照从大到小排列,即队首元素最大
priority_queue<int, vector<int>, greater<int> > min; //有限队列,greater保证队列中元素按照从小到大排列,即队首元素最小

void Insert(int num)
{
if(max.empty()||num<=max.top())
max.push(num);
else
min.push(num);

//保证max与min中元素个数均衡
if(max.size()==min.size()+2){
min.push(max.top());
max.pop();
}
//如果元素个数为奇数,保证max的队首元素为中位数
if(max.size()+1==min.size()){
max.push(min.top());
min.pop();
}

}

double GetMedian()
{
return max.size()==min.size()?(max.top()+min.top())/2.0 : max.top();

}

};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  剑指Offer