关于优先队列priority_queue大小根堆、重载操作符的说明
關(guān)于priority_queue的說(shuō)明
內(nèi)部實(shí)現(xiàn)
priority_queue 默認(rèn)情況下,以vector 為底層容器,加上heap(默認(rèn)max-heap) 處理規(guī)則;形成大根堆。
priority_queue被歸為 container adapter,也就是對(duì) container 進(jìn)行封裝一層。
priority_queue 操作規(guī)則上是 queue,只允許在尾部加入元素,并從首部取出元素;只不過(guò)內(nèi)部元素具有優(yōu)先級(jí),優(yōu)先級(jí)高者先出。
priority_queue 的所有元素進(jìn)出具有一定規(guī)則,所以不提供遍歷功能,也不提供迭代器。
疑惑產(chǎn)生
下面為priority_queue 的使用規(guī)則,第一個(gè)傳入了類型,第二個(gè)為容器類型,第三個(gè)為比較函數(shù)。
template<
class T,
class Container = std::vector<T>,
class Compare = std::less<typename Container::value_type> //comp默認(rèn)為less
> class priority_queue;
疑惑關(guān)鍵就在于比較函數(shù)。
priority_queue 默認(rèn)形成大根堆,而傳入的comp默認(rèn)為less。
為何傳入一個(gè)可將序列順序調(diào)為有小到大的函數(shù),建成的堆反而是大頂堆呢?
不知你們有沒(méi)有這種感覺(jué)?直覺(jué)上認(rèn)為傳入less,建成小頂堆,而傳入greater,建成大頂堆。
源碼解析
std::less()源碼:若__x < __y,則返回true,順序不變,否則,順序發(fā)生變化。這個(gè)函數(shù)名含義與實(shí)現(xiàn)效果相一致。
struct less : public binary_function<_Tp, _Tp, bool>
{
_GLIBCXX14_CONSTEXPR
bool
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x < __y; }
};
make_heap中調(diào)用的__push_heap源碼:
__push_heap(_RandomAccessIterator __first, _Distance __holeIndex,
_Distance __topIndex, _Tp __value, _Compare __comp)
//__holeIndex 新添加節(jié)點(diǎn)的索引,即叫做孔洞
//__topIndex 頂端索引
//__value 新添加節(jié)點(diǎn)的值
//__comp 比較函數(shù),傳入為less
{
_Distance __parent = (__holeIndex - 1) / 2; //找到新節(jié)點(diǎn)父節(jié)點(diǎn)索引
while (__holeIndex > __topIndex && __comp(*(__first + __parent), __value)) {
//若孔洞沒(méi)有到達(dá)最頂端 && 父節(jié)點(diǎn)的值小于新添加節(jié)點(diǎn)的值,則要進(jìn)行下列操作
//less中,左邊比右邊小則返回true,與less愿意相同
*(__first + __holeIndex) = *(__first + __parent); //將父節(jié)點(diǎn)的值放入孔洞
__holeIndex = __parent; //孔洞的索引編程了原來(lái)父節(jié)點(diǎn)的索引,往上移動(dòng)了
__parent = (__holeIndex - 1) / 2; //那么孔洞的父節(jié)點(diǎn)又要繼續(xù)往上找
}
*(__first + __holeIndex) = __value; //將新添加節(jié)點(diǎn)的值放在找到的位置(孔洞)
}
經(jīng)過(guò)上面源碼分析,傳入的comp就是為了在比較孔洞節(jié)點(diǎn)與父節(jié)點(diǎn)的大小,若返回true,才會(huì)進(jìn)行交換操作。
所以傳入less,返回true是因?yàn)楦腹?jié)點(diǎn)比孔洞節(jié)點(diǎn)小,所以要進(jìn)行交換,則將大的值移動(dòng)到前面,所以建成的堆為大頂堆。
而傳入greater,返回true是因?yàn)楦腹?jié)點(diǎn)比孔洞節(jié)點(diǎn)答,所以進(jìn)行叫喊,則將大的值移動(dòng)到了后面,所以建成的堆為小頂堆。
所以,就明白了為什么傳入less反而形成了大根堆,而傳入greater則形成了小根堆。
如何使用
#include <queue> using namespace std; ? priority_queue<int> que; //默認(rèn)定義了最大堆,等同于將第三個(gè)參數(shù)使用less<int> priority_queue<int, vector<int>, less<int>> que; //定義大根堆 ? priority_queue<int, vector<int>, greater<int>> que; //定義小根堆,VS下需要加入頭文件#include<functional> ? //測(cè)試 priority_queue<int> que; que.push(3); que.push(5); que.push(4); cout << que.top() << endl; //5 ? //測(cè)試 priority_queue<int, vector<int>, less<int>> que; que.push(3); que.push(5); que.push(4); cout << que.top() << endl; //5 ? //測(cè)試 priority_queue<int, vector<int>, greater<int>> que; que.push(3); que.push(5); que.push(4); cout << que.top() << endl; //3
關(guān)于自定義優(yōu)先級(jí)
上面給出了在處理整型等基本數(shù)據(jù)類型時(shí)直接出入less或者greater既可以建立相應(yīng)的大頂堆或者小頂堆。若是處理結(jié)構(gòu)體呢?如何定義優(yōu)先級(jí)呢?
普通數(shù)據(jù)類型
基本數(shù)據(jù)類型的比較函數(shù)可以直接使用less<int>或者greater<int>可以滿足建立大根堆或者小根堆。
結(jié)構(gòu)體
對(duì)于結(jié)構(gòu)體而言,將結(jié)構(gòu)體放入優(yōu)先隊(duì)列中,比較函數(shù)需要建立在針對(duì)結(jié)構(gòu)體的具體成員。
參考https://www.cnblogs.com/flipped/p/5691430.html 博客自定義優(yōu)先級(jí)。
自定義優(yōu)先級(jí)的三種方法:
1.<以成員函數(shù)重載:
struct Node { //我們將Node節(jié)點(diǎn)放入優(yōu)先隊(duì)列中希望以value進(jìn)行比較
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 > (),結(jié)果會(huì)報(bào)二進(jìn)制“<”: 沒(méi)有找到接受“const Node”類型的左操作數(shù)的運(yùn)算符(或沒(méi)有可接受的轉(zhuǎn)換) 錯(cuò)誤,我想原因應(yīng)該是這樣吧:priority_queue中默認(rèn)的比較函數(shù)為less,less函數(shù)中只用到了 { return __x < __y; },所以重載中若只重載了>,函數(shù)找不到<,所以會(huì)出現(xiàn)錯(cuò)誤。
struct less : public binary_function<_Tp, _Tp, bool>
{
_GLIBCXX14_CONSTEXPR
bool
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x < __y; }
};
2.自定義比較函數(shù):
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
上述在傳入用戶自定義的比較函數(shù),那么在建堆過(guò)程中使用的comp函數(shù)即為我們自定義的cmp,這樣分析同上。
3.<以類成員函數(shù)重載
struct Node { //我們將Node節(jié)點(diǎn)放入優(yōu)先隊(duì)列中希望以value進(jìn)行比較
Node(int _id, int _value) : id(_id), value(_value){}
int id;
int value;
//大根堆
bool operator < (const Node& b) const //注意,此處若沒(méi)有const則會(huì)報(bào)錯(cuò)
{
return value < b.value; //將value的值由大到小排列,形成Node的大根堆
}
};
cout << que.top().value << endl; //5
4.<以友元函數(shù)重載
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;
剛開(kāi)始不太明白友元操作符重載函數(shù),先了解下重載與友元。
重載類型
參考:https://www.cnblogs.com/Mayfly-nymph/p/9034936.html
綜合,在C++中,操作符重載實(shí)現(xiàn)通過(guò)類成員函數(shù)、全局函數(shù)(非類成員函數(shù))與友元函數(shù)(不是類成員卻能夠訪問(wèn)類的所有成員 )。
上面對(duì)<重載使用到類全局函數(shù)、友元函數(shù)、類成員函數(shù)。
使用友元函數(shù)重載有兩個(gè)優(yōu)點(diǎn):和普通函數(shù)重載相比,它能夠訪問(wèn)類的非公有成員與將雙目運(yùn)算符重載為友元函數(shù),這樣就可以使用交換律。
交換律可以理解成復(fù)數(shù)加法中,以成員函數(shù)的形式重載 +,只能計(jì)算c + 15.6,不能計(jì)算28.23 + c,這是不對(duì)稱的 。
何為友元
友元函數(shù)
類的友元函數(shù)是定義在類外部,但有權(quán)訪問(wèn)類的所有私有(private)成員和保護(hù)(protected)成員。
盡管友元函數(shù)的原型有在類的定義中出現(xiàn)過(guò),但是友元函數(shù)并不是成員函數(shù)。
友元可以是一個(gè)函數(shù),該函數(shù)被稱為友元函數(shù);
友元也可以是一個(gè)類,該類被稱為友元類,在這種情況下,整個(gè)類及其所有成員都是友元。
如果要聲明函數(shù)為一個(gè)類的友元,需要在類定義中該函數(shù)原型前使用關(guān)鍵字 friend。如下設(shè)置友元函數(shù):
class Box
{
double width;
public:
double length;
friend void printWidth( Box box );
void setWidth( double wid );
};
聲明類 Usb 的所有成員函數(shù)作為類 Phone 的友元,需要在類 Phone 的定義中放置如下聲明:
class Usb
{
private:
int size;
friend class Phone; //聲明 Phone為友元類
};
class Phone
{
public:
Usb usb;
void setPhone()
{
usb.size += 1000; //因Phone是Usb的友元類,故此處可以訪問(wèn)其私有成員
}
};
總結(jié)
以上是生活随笔為你收集整理的关于优先队列priority_queue大小根堆、重载操作符的说明的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 理想ONE再升级!新增17项功能 优化1
- 下一篇: 学习的网址