C++ STL priority_queue容器適配器詳解

priority_queue 容器適配器模擬的也是隊列這種存儲結構,即使用此容器適配器存儲元素只能“從一端進(稱爲隊尾),從另一端出(稱爲隊頭)”,且每次只能訪問 priority_queue 中位於隊頭的元素。

但是,priority_queue 容器適配器中元素的存和取,遵循的並不是 “First in,First out”(先入先出)原則,而是“First in,Largest out”原則。直白的翻譯,指的就是先進隊列的元素並不一定先出隊列,而是優先級最大的元素最先出隊列。

注意,“First in,Largest out”原則是筆者爲了總結 priority_queue 存取元素的特性自創的一種稱謂,僅爲了方便讀者理解。

那麼,priority_queue 容器適配器中存儲的元素,優先級是如何評定的呢?很簡單,每個 priority_queue 容器適配器在創建時,都制定了一種排序規則。根據此規則,該容器適配器中存儲的元素就有了優先級高低之分。

舉個例子,假設當前有一個 priority_queue 容器適配器,其制定的排序規則是按照元素值從大到小進行排序。根據此規則,自然是 priority_queue 中值最大的元素的優先級最高。

priority_queue 容器適配器爲了保證每次從隊頭移除的都是當前優先級最高的元素,每當有新元素進入,它都會根據既定的排序規則找到優先級最高的元素,並將其移動到隊列的隊頭;同樣,當 priority_queue 從隊頭移除出一個元素之後,它也會再找到當前優先級最高的元素,並將其移動到隊頭。

基於 priority_queue 的這種特性,因此該容器適配器有被稱爲優先級隊列。

priority_queue 容器適配器“First in,Largest out”的特性,和它底層採用堆結構存儲數據是分不開的。有關該容器適配器的底層實現,後續章節會進行深度剖析。

STL 中,priority_queue 容器適配器的定義如下:

template <typename T,

        typename Container=std::vector<T>,

        typename Compare=std::less<T> >

class priority_queue{

    //......

}

可以看到,priority_queue 容器適配器模板類最多可以傳入 3 個參數,它們各自的含義如下:

  • typename T:指定存儲元素的具體類型;
  • typename Container:指定 priority_queue 底層使用的基礎容器,默認使用 vector 容器。

作爲 priority_queue 容器適配器的底層容器,其必須包含 empty()、size()、front()、push_back()、pop_back() 這幾個成員函數,STL 序列式容器中只有 vector 和 deque 容器符合條件。

  • typename Compare:指定容器中評定元素優先級所遵循的排序規則,默認使用std::less按照元素值從大到小進行排序,還可以使用std::greater按照元素值從小到大排序,但更多情況下是使用自定義的排序規則。

其中,std::less 和 std::greater 都是以函數對象的方式定義在 頭文件中。關於如何自定義排序規則,後續章節會做詳細介紹。

創建priority_queue的幾種方式

由於 priority_queue 容器適配器模板位於頭文件中,並定義在 std 命名空間裏,因此在試圖創建該類型容器之前,程序中需包含以下 2 行代碼:

# include <queue>
using namespace std;

創建 priority_queue 容器適配器的方法,大致有以下幾種。

    1. 創建一個空的 priority_queue 容器適配器,第底層採用默認的 vector 容器,排序方式也採用默認的 std::less 方法:
std::priority_queue<int> values;
  1. 可以使用普通數組或其它容器中指定範圍內的數據,對 priority_queue 容器適配器進行初始化:
//使用普通數組
int values[]{4,1,3,2};
std::priority_queue<int>copy_values(values,values+4);//{4,2,3,1}
//使用序列式容器
std::array<int,4>values{ 4,1,3,2 };
std::priority_queue<int>copy_values(values.begin(),values.end());//{4,2,3,1}

注意,以上 2 種方式必須保證數組或容器中存儲的元素類型和 priority_queue 指定的存儲類型相同。另外,用來初始化的數組或容器中的數據不需要有序,priority_queue 會自動對它們進行排序。

  1. 還可以手動指定 priority_queue 使用的底層容器以及排序規則,比如:
int values[]{ 4,1,2,3 };
std::priority_queue<int, std::deque<int>, std::greater<int> >copy_values(values, values+4);//{1,3,2,4}

事實上,std::less 和 std::greater 適用的場景是有限的,更多場景中我們會使用自定義的排序規則。

由於自定義排序規則的方式不只一種,因此這部分知識將在後續章節做詳細介紹。

priority_queue提供的成員函數

priority_queue 容器適配器提供了表 2 所示的這些成員函數。

成員函數 功能
empty() 如果 priority_queue 爲空的話,返回 true;反之,返回 false。
size() 返回 priority_queue 中存儲元素的個數。
top() 返回 priority_queue 中第一個元素的引用形式。
push(const T& obj) 根據既定的排序規則,將元素 obj 的副本存儲到 priority_queue 中適當的位置。
push(T&& obj) 根據既定的排序規則,將元素 obj 移動存儲到 priority_queue 中適當的位置。
emplace(Args&&… args) Args&&… args 表示構造一個存儲類型的元素所需要的數據(對於類對象來說,可能需要多個數據構造出一個對象)。此函數的功能是根據既定的排序規則,在容器適配器適當的位置直接生成該新元素。
pop() 移除 priority_queue 容器適配器中第一個元素。
swap(priority_queue& other) 將兩個 priority_queue 容器適配器中的元素進行互換,需要注意的是,進行互換的 2 個 priority_queue 容器適配器中存儲的元素類型以及底層採用的基礎容器類型,都必須相同。

和 queue 一樣,priority_queue 也沒有迭代器,因此訪問元素的唯一方式是遍歷容器,通過不斷移除訪問過的元素,去訪問下一個元素。

下面的程序演示了表 2 中部分成員函數的具體用法:

# include <iostream>
# include <queue>
# include <array>
# include <functional>
using namespace std;
int main()
{
    //創建一個空的priority_queue容器適配器
    std::priority_queue<int>values;
    //使用 push() 成員函數向適配器中添加元素
    values.push(3);//{3}
    values.push(1);//{3,1}
    values.push(4);//{4,1,3}
    values.push(2);//{4,2,3,1}
    //遍歷整個容器適配器
    while (!values.empty())
    {
        //輸出第一個元素並移除。
        std::cout << values.top()<<" ";
        values.pop();//移除隊頭元素的同時,將剩餘元素中優先級最大的移至隊頭
    }
    return 0;
}

運行結果爲:

4 3 2 1

表 2 中其它成員函數的用法也非常簡單,這裏不再給出具體示例,後續章節用法會做具體介紹。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章