一、關于\(priority\_queue\)的說明
内部實作
priority_queue
預設情況下,以\(vector\)為底層容器,加上\(heap\)(預設\(max-heap\)) 處理規則;形成大根堆。
\(priority\_queue\)被歸為 \(container\) \(adapter\),也就是對 \(container\)
priority_queue
操作規則上是 \(queue\),隻允許在尾部加入元素,并從首部取出元素;隻不過内部元素具有優先級,優先級高者先出。
priority_queue
的所有元素進出具有一定規則,是以 不提供周遊功能,也不提供疊代器。
疑惑産生
下面為
priority_queue
的使用規則,第一個傳入了類型,第二個為容器類型,第三個為比較函數。
template<
class T,
class Container = std::vector<T>,
class Compare = std::less<typename Container::value_type> //comp預設為less
> class priority_queue;
疑惑關鍵就在于比較函數。
priority_queue
預設形成大根堆,而傳入的\(comp\)預設為\(less\)。
為何傳入一個可将序列順序調為有小到大的函數,建成的堆反而是大頂堆呢?
不知你們有沒有這種感覺?直覺上認為傳入\(less\),建成小頂堆,而傳入\(greater\),建成大頂堆。
源碼解析
std::less()
源碼:若
__x < __y
,則傳回\(true\),順序不變,否則,順序發生變化。這個函數名含義與實作效果相一緻。
struct less : public binary_function<_Tp, _Tp, bool>
{
_GLIBCXX14_CONSTEXPR
bool
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x < __y; }
};
make_heap
中調用的
__push_heap
源碼:
__push_heap(_RandomAccessIterator __first, _Distance __holeIndex,
_Distance __topIndex, _Tp __value, _Compare __comp)
//__holeIndex 新添加節點的索引,即叫做孔洞
//__topIndex 頂端索引
//__value 新添加節點的值
//__comp 比較函數,傳入為less
{
_Distance __parent = (__holeIndex - 1) / 2; //找到新節點父節點索引
while (__holeIndex > __topIndex && __comp(*(__first + __parent), __value)) {
//若孔洞沒有到達最頂端 && 父節點的值小于新添加節點的值,則要進行下列操作
//less中,左邊比右邊小則傳回true,與less願意相同
*(__first + __holeIndex) = *(__first + __parent); //将父節點的值放入孔洞
__holeIndex = __parent; //孔洞的索引程式設計了原來父節點的索引,往上移動了
__parent = (__holeIndex - 1) / 2; //那麼孔洞的父節點又要繼續往上找
}
*(__first + __holeIndex) = __value; //将新添加節點的值放在找到的位置(孔洞)
}
經過上面源碼分析,傳入的\(comp\)就是為了在比較 孔洞節點 與 父節點 的大小,若傳回\(true\), 才會進行交換操作 。
是以傳入\(less\),傳回\(true\)是因為 父節點比孔洞節點小,是以要進行交換,則将大的值移動到前面,是以建成的堆為 大頂堆。
而傳入\(greater\),傳回\(true\)是因為 父節點 比 孔洞節點大,是以進行交換,則将大的值移動到了後面,是以建成的堆為 小頂堆。
是以,就明白了為什麼傳入\(less\)反而形成了大根堆,而傳入\(greater\)則形成了小根堆。
二、如何使用
#include <queue>
using namespace std;
priority_queue<int> que; //預設定義了最大堆,等同于将第三個參數使用less<int>
priority_queue<int, vector<int>, less<int>> que; //定義大根堆
priority_queue<int, vector<int>, greater<int>> que; //定義小根堆,VS下需要加入頭檔案#include<functional>
//測試 priority_queue<int> que;
que.push(3);
que.push(5);
que.push(4);
cout << que.top() << endl; //5
//測試 priority_queue<int, vector<int>, less<int>> que;
que.push(3);
que.push(5);
que.push(4);
cout << que.top() << endl; //5
//測試 priority_queue<int, vector<int>, greater<int>> que;
que.push(3);
que.push(5);
que.push(4);
cout << que.top() << endl; //3
三、關于自定義優先級
上面給出了在處理整型等基本資料類型時直接出入\(less\)或者\(greater\)既可以建立相應的大頂堆或者小頂堆。若是處理結構體呢?如何定義優先級呢?
普通資料類型
基本資料類型的比較函數可以直接使用
less<int>
或者
greater<int>
可以滿足建立大根堆或者小根堆。
結構體
對于結構體而言,将結構體放入優先隊列中,比較函數需要建立在針對結構體的具體成員。
自定義優先級的四種方法:
- <以成員函數重載
struct Node { //我們将Node節點放入優先隊列中希望以value進行比較
Node(int _id, int _value) : id(_id), value(_value){}
int id;
int value;
};
//大根堆
bool operator < (const Node& a, const Node& b){
return a.value < b.value; //将value的值由大到小排列,形成Node的大根堆
}
int main() {
struct Node node1(1, 5);
struct Node node2(2, 3);
struct Node node3(3, 4);
priority_queue<Node> que;
que.push(node1);
que.push(node2);
que.push(node3);
cout << que.top().value << endl; //5
}
//小根堆
bool operator < (const Node& a, const Node& b)
{
return a.value > b.value; //将value的值由小到大排列,形成Node的小根堆
}
cout << que.top().value << endl; //3
我試了
bool operator > ()
,結果會報二進制“<”: 沒有找到接受
const Node
類型的左操作數的運算符(或沒有可接受的轉換) 錯誤,我想原因應該是這樣吧:
priority_queue
中預設的比較函數為
less
,
less
函數中隻用到了
{ return __x < __y; }
,是以重載中若隻重載了
>
,函數找不到
<
,是以會出現錯誤。
struct less : public binary_function<_Tp, _Tp, bool>{
_GLIBCXX14_CONSTEXPR
bool
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x < __y; }
};
- 自定義比較函數
struct cmp{
bool operator ()(const Node& a, const Node& b){
return a.value < b.value;//将value的值由大到小排列,形成Node的大根堆
}
};
priority_queue<Node, vector<Node>, cmp>q;
cout << que.top().value << endl; //5
struct cmp{
bool operator ()(const Node& a, const Node& b){
return a.value > b.value;//将value的值由小到大排列,形成Node的小根堆
}
};
priority_queue<Node, vector<Node>, cmp>q;
cout << que.top().value << endl; //3
- < 以類成員函數重載
struct Node { //我們将Node節點放入優先隊列中希望以value進行比較
Node(int _id, int _value) : id(_id), value(_value){}
int id;
int value;
//大根堆
bool operator < (const Node& b) const //注意,此處若沒有const則會報錯
{
return value < b.value; //将value的值由大到小排列,形成Node的大根堆
}
};
cout << que.top().value << endl; //5
- <以友元函數重載 【這個和競賽關系不大,暫時不用考慮】
struct Node{
int id;
int value;
friend bool operator<(const Node& a,const Node& b){
return a.value<b.value; //按value從大到小排列
}
};
priority_queue<Node>q;