頭文件#include<queue>
。
其實就是堆,可以這麼說。對於有的問題需要用堆去實現的,就可以用優先隊列。
它允許用戶爲隊列中元素設置優先級,放置元素的時候不是直接放到隊尾,而是放置到比它優先級低的元素前面,標準庫默認使用 < 操作符來確定優先級關係。
priority_queue與queue一樣,也是從隊尾添加元素,從隊頭刪除元素。因元素放置順序是按元素優先級來的,所以出隊出的是最高優先級的元素。優先隊列具有 最高優先級先出的行爲特性。
它的原型是:
template <class T, class Container = vector<T>, class Compare = less<typename Container::value_type> > class priority_queue;
第一個爲:元素類型
第二個爲:承載優先隊列的容器類型。默認是vector<>
第三個爲:比較函數。帶默認是less<>
注:defaultpriority_queue<T, vector<T>, less<T>>
情況下:優先隊列的優先級關係爲值大的優先級高、值小的優先級低,而優先級高的放在隊列前面,所以對於默認類型,它的內部元素總是從大到小的。
定義
1、使用系統類型(如int)
如定義:priority_queue<int> pq;
等價於priority_queue<int, vector<int>, less<int>> pq;
依次push1-5-8-2-9,一直top取隊頭元素並pop彈出,直到隊空。輸出順序會是:9-8-5-2-1
默認從小到大。值大的數優先級高,放在前面。
如果要改變優先級呢?來按從小到大排呢
這樣定義:priority_queue<int, vector<int>, greater<int>> pq;//3個int要一致
2、自定義類型
使用自定義類型,就要重載<運算符
。
#include<queue>
#include<iostream>
using namespace std;
struct BTNode{
int priority;
int value;
bool operator < (const node &a, const node &b) {
return a.priority < b.priority;
}
常用操作
- 添加:
pq.push(x)
- 刪除/彈出:
pq.pop()
- 取隊頭元素:
x=pq.top()
注意:這裏與一般的queue不一樣,不用front()!!! - 判空:
pq.empty()
- 取大小:
pq.size()
應用
關於可以用堆去實現的都可以用優先隊列priority_queue。
堆排序
由於每次出隊都是在剩下元素裏面最大(小)的,所以只要把數組的元素放到一個priority_queue裏,然後依次top+pop,得到的序列就是排序好的。
n個元素做排序,堆大小也爲n。不管是插入還是刪除操作,每次調整的複雜度爲log(n)(堆的高度),所以算法的時間複雜度就是O(nlogn)。有人說,實際使用時候效率比快排歸併排序略差,待研究。
top K
找一波數字裏面最大的k個數字。
用一個k大小的優先隊列。小頂堆,堆頂爲整個隊列中的最小值
遍歷整個數據:
if pq.size()<k: 添加到pq裏
if pq.size()==k:
比較各num和pq.top()的大小,如果大於則替換堆頂,即彈出隊頭(堆頂),然後插入到堆裏面。
數據流中的中位數
之前我在 https://blog.csdn.net/u013317445/article/details/89680330 c++重拾 STL之heap(堆)這篇博客中用heap實現過。今天我用priority_queue優先隊列實現一次。
**問題:**如何得到一個數據流中的中位數?如果從數據流中讀出奇數個數值,那麼中位數就是所有數值排序之後位於中間的數值。如果從數據流中讀出偶數個數值,那麼中位數就是所有數值排序之後中間兩個數的平均值。我們使用Insert()方法讀取數據流,使用GetMedian()方法獲取當前讀取數據的中位數。
思路:
將數據分爲兩個部分,左半部分、右半部分。
左半部分用大頂堆,保證左半部分數據的最大值在大頂堆的頂部。
右半部分用小頂堆,保證右半部分數據的最小值在小頂堆的頂部。
當數據總數爲偶數時候,將來的num加入到右半部分,
總數爲奇數時候,將來的num加入到左半部分。
整個數據流的中位數就看堆頂。
更具體點,當數據總數爲偶數時候,爲兩部分堆頂的平均值,
爲奇數時候,就是左半部分的堆頂。
調整:
1、當總數偶數時,本計劃來的數加入到右半部分(小頂堆)。
但是如果來的數字比大頂堆的最大值還小,所以理應加入到左半部分(大頂堆),
但是左半部分又多了一個數,所以左半部分刪除最大值,並將其添加到右半部分(小頂堆),來保證左右部分數字個數平分。
否則的話,來的數就直接加入到右半部分。
2、當總數爲奇數時,本計劃...但... 同理。
class Solution {
private:
priority_queue<int, vector<int>, less<int>> max;//大頂堆(左半部分,堆頂要爲左的最大值)
priority_queue<int, vector<int>, greater<int>> min;//小頂堆(右半部分,堆頂要爲右的最小值)
public:
void Insert(int num)
{
if( ((max.size()+min.size())&1) ==0){//總數爲偶數
int maxNum= max.top();//即大頂堆的最大值(左半部分的最大值)
if(max.size()>0 && num < maxNum){//犯錯:必須寫上max.size()>0 後面要max.pop()呀
max.push(num);
min.push(max.top());
max.pop();
}else{
min.push(num);
}
}else{//總數爲奇數,本計劃加入到左半部分... .. 和上面同理
int minNum= min.top();//即小頂堆的最小值(右半部分的最小值)
if(min.size()>0 && num > minNum){ //犯錯:必須寫上min.size()>0
min.push(num);
max.push(min.top());
min.pop();
}else{
max.push(num);
}
}
}
double GetMedian()
{
//下面這個一直報錯,段錯誤 下面的code不就多了一個定義double嗎?問題到底在哪裏 還少考慮了什麼情況呢?
// if((max.size()+min.size())==0) return 0;
// return ((max.size()+ min.size())&1 )==0 ? (max.top()+min.top())/2.0 : max.top();//必須是2.0 不是2
double median=0;
if((max.size()+min.size())==0)
return 0;
if(((max.size()+min.size()) &1)==0)
{
median= (max.top()+min.top())/2.0;//2.0 或者給分子的每一個加強轉(double)
}else
{
median= min.top();
}
return median;
}
};