整理自劍指Offer
牛客網https://www.nowcoder.com/questionTerminal/9be0172896bd43948f8a32fb954e1be1
一:題目描述
如何得到一個數據流中的中位數?如果從數據流中讀出奇數個數值,那麼中位數就是所有數值排序之後位於中間的數值。如果從數據流中讀出偶數個數值,那麼中位數就是所有數值排序之後中間兩個數的平均值。
二:解題思路
由於數據從一個數據流中讀出來的,數據的數目隨着時間的變化而增加,如何選擇一個容器存儲數據,使插入一個數據後有序 與 計算中位數的時間複雜度性能最優?
如果利用兩個指針去尋找中位數
N1 | N2 | ... | N2m | N2m+1 |
奇數:P1與P2指向同一個位置
N1 | N2 | ... | 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();
}
};