淺談C++ STL中的優先隊列(priority_queue)

從我之前的博文能看出來,我是一個隊列愛好者,不少並非必定須要用隊列實現的算法我也會採用隊列實現,主要是因爲隊列和人的直覺思惟的一致性致使的。算法

今天講一講優先隊列(priority_queue),實際上,它的本質就是一個heap,我從STL中扒出了它的實現代碼,你們能夠參考一下。less

首先函數在頭文件<queue>中,歸屬於命名空間std,使用的時候須要注意。函數

隊列有兩種經常使用的聲明方式:性能

std::priority_queue<T> pq;
std::priority_queue<T, std::vector<T>, cmp> pq;

 

第一種實現方式較爲經常使用,接下來我給出STL中的對應聲明,再加以解釋。spa

template<class _Ty,
    class _Container = vector<_Ty>,
    class _Pr = less<typename _Container::value_type> >
    class priority_queue

 

你們能夠看到,默認模板有三個參數,第一個是優先隊列處理的類,第二個參數比較有特色,是容納優先隊列的容器。實際上,優先隊列是由這個容器+C語言中關於heap的相關操做實現的。這個容器默認是vector,也能夠是dequeue,由於後者功能更強大,而性能相對於vector較差,考慮到包裝在優先隊列後,後者功能並不能很好發揮,因此通常選擇vector來作這個容器。第三個參數比較重要,支持一個比較結構,默認是less,默認狀況下,會選擇第一個參數決定的類的<運算符來作這個比較函數。code

接下來開始坑爹了,雖然用的是less結構,然而,隊列的出隊順序倒是greater的先出!就是說,這裏這個參數其實很傲嬌,表示的意思是若是!cmp,則先出列,無論這樣實現的目的是啥,你們只能接受這個實現。實際上,這裏的第三個參數能夠更換成greater,像下面這樣:blog

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小的先出列,怎麼辦,就要很違和的給這個小於符號重載成其實是大於的定義。element

若是咱們不使用自定義類,又要用非默認方法去排序怎麼辦?就好比說在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();
        }
 
相關文章
相關標籤/搜索