從我以前的博文能看出來,我是一個隊列愛好者,很多并不是一定需要用隊列實現的算法我也會采用隊列實現,主要是由于隊列和人的直覺思維的一致性導致的。
今天講一講優先隊列(PRiority_queue),實際上,它的本質就是一個heap,我從STL中扒出了它的實現代碼,大家可以參考一下。
首先函數在頭文件<queue>中,歸屬于命名空間std,使用的時候需要注意。
隊列有兩種常用的聲明方式:
std::priority_queue<T> pq;std::priority_queue<T, std::vector<T>, cmp> pq;
第一種實現方式較為常用,接下來我給出STL中的對應聲明,再加以解釋。
template<class _Ty, class _Container = vector<_Ty>, class _Pr = less<typename _Container::value_type> > class priority_queue
大家可以看到,默認模板有三個參數,第一個是優先隊列處理的類,第二個參數比較有特點,是容納優先隊列的容器。實際上,優先隊列是由這個容器+C語言中關于heap的相關操作實現的。這個容器默認是vector,也可以是dequeue,因為后者功能更強大,而性能相對于vector較差,考慮到包裝在優先隊列后,后者功能并不能很好發揮,所以一般選擇vector來做這個容器。第三個參數比較重要,支持一個比較結構,默認是less,默認情況下,會選擇第一個參數決定的類的<運算符來做這個比較函數。
接下來開始坑爹了,雖然用的是less結構,然而,隊列的出隊順序卻是greater的先出!就是說,這里這個參數其實很傲嬌,表示的意思是如果!cmp,則先出列,不管這樣實現的目的是啥,大家只能接受這個實現。實際上,這里的第三個參數可以更換成greater,像下面這樣:
std::priority_queue<T, std::vector<T>, greater<T>> pq;
一般大家如果是自定義類就干脆重載<號時注意下方向了,沒人在這里麻煩,這個選擇基本上是在使用int類還想小值先出列時。
從上面的剖析我們也就知道了,想要讓自定義類能夠使用優先隊列,我們要重載小于號。
class Student{ int id; char name[20]; bool gender; bool Operator < (Student &a) const { return id > a.id; }};
就拿這個例子說,我們想讓id小的先出列,怎么辦,就要很違和的給這個小于符號重載成實際上是大于的定義。
如果我們不使用自定義類,又要用非默認方法去排序怎么辦?就比如說在Dijkstra中,我們當然不會用點的序號去排列,無論是正序還是反序,我們想用點到起點的距離這個值來進行排序,我們怎樣做呢?細心的讀者在閱讀我的有關Dijkstra那篇文章時應該就發現了做法——自定義比較結構。優先隊列默認使用的是小于結構,而上文的做法是為我們的自定義類去定義新的小于結構來符合優先隊列,我們當然也可以自定義比較結構。自定義方法以及使用如下,我直接用Dijkstra那篇的代碼來說明:
int cost[MAX_V][MAX_V];int d[MAX_V], V, s;//自定義優先隊列less比較函數struct cmp{ bool operator()(int &a, int &b) const { //因為優先出列判定為!cmp,所以反向定義實現最小值優先 return d[a] > d[b]; }};void Dijkstra(){ std::priority_queue<int, std::vector<int>, cmp> pq; pq.push(s); d[s] = 0; while (!pq.empty()) { int tmp = pq.top();pq.pop(); for (int i = 0;i < V;++i) { if (d[i] > d[tmp] + cost[tmp][i]) { d[i] = d[tmp] + cost[tmp][i]; pq.push(i); } } }}
優先隊列的日常使用,了解上面那些就已經足夠。下面給出優先隊列的所有成員函數的STL實現方法,希望你看完沒有一臉臥槽的感覺。c就是你聲明時候的那個vector或者其他容器。
void push(value_type&& _Val) { // insert element at beginning c.push_back(_STD move(_Val)); push_heap(c.begin(), c.end(), comp); } template<class... _Valty> void emplace(_Valty&&... _Val) { // insert element at beginning c.emplace_back(_STD forward<_Valty>(_Val)...); push_heap(c.begin(), c.end(), comp); } bool empty() const { // test if queue is empty return (c.empty()); } size_type size() const { // return length of queue return (c.size()); } const_reference top() const { // return highest-priority element return (c.front()); } void push(const value_type& _Val) { // insert value in priority order c.push_back(_Val); push_heap(c.begin(), c.end(), comp); } void pop() { // erase highest-priority element pop_heap(c.begin(), c.end(), comp); c.pop_back(); }
新聞熱點
疑難解答
圖片精選