前引:?在算法競賽中,選手們常常能在0.01秒內分出勝負;在實時交易系統中,毫秒級的延遲可能意味著數百萬的盈虧;在高并發服務器中,每秒需要處理數萬條不同優先級的請求——這些系統背后,都隱藏著同一種強大的數據結構:?優先隊列(priority_queue)?作為C++標準庫中最優雅的數據結構適配器,
priority_queue
完美封裝了堆算法的高效性,卻只需幾行代碼即可實現復雜優先級管理。本文將深入剖析:(1)?堆原理與優先隊列的機械美學?
(2)定制化優先級策略的高級技巧?
(3)實戰性能對比與編譯器級優化?
(4)在萬億級數據處理中的獨特優勢
目錄
優先隊列介紹
優先隊列的淵源
實例化
插入元素
訪問元素
獲取元素個數
判斷優先隊列是否為空
刪除堆頂元素
·
優先隊列的模擬實現
類模板
插入元素
訪問元素
獲取元素個數
判斷優先隊列是否為空
刪除堆頂元素
效果展示
優先隊列介紹
優先隊列priority_queue
是 C++ 標準模板庫(STL)中的一個容器適配器,提供了堆數據結構的實現(默認為最大堆)。它在需要高效訪問最大/最小元素的場景下非常有用!
如果需要使用小頂堆,可以這樣傳參 priority_queue< int , vector<int> , greater<int> >?
它是默認基于(大頂)堆實現的,例如一顆用數組存儲的完全二叉樹:
特點總結:
(1)采用數組形式存儲
(2)默認基于最大堆實現
(3)適配器容器底層為 vector?(使用需要包含#include<queue>)?
(4)每次只能訪問隊列頂部的元素,即優先級最高的元素
(5)復雜度:訪問O(1)、插入O(log n)、刪除頂部元素O(log n)
優先隊列的淵源
我們通過 優先隊列 的容器結構應該猜到,它的底層容器是 vector ,為什么不取名叫優先維克托呢
問:為什么底層容器是vector?
連續內存結構適合堆的隨機訪問需求,緩存友好,且動態數組支持高效尾部操作
問:為什么頭文件是queue?
作為容器適配器,優先隊列在概念上屬于隊列的一種,與queue共用同一頭文件,體現了接口的一致性,比如隊列的各種接口剛好吻合它的訪問、插入、刪除行為:
priority_queue --> "<queue>頭文件" : 聲明接口
問:為什么叫優先隊列而不是優先維克托?
名稱語義分解?:
優先(Priority):元素按內在重要性排序,而非插入順序
隊列(Queue):僅允許特定端點訪問的操作模型(隊尾插入,隊首訪問)
?行為本質?:
插入操作:push()
,時間復雜度 O(log n)
訪問操作:top()
,總是獲取優先級最高的元素
刪除操作:pop()
,移除當前最高優先級元素
實例化
采用:priority_queue<數據類型> 變量名;
我們可以選擇默認初始化:
priority_queue<int> V1;
也可以選擇范圍初始化:
priority_queue<int> V2(arr,arr+n);
//或者用另一個容器去初始化
priority_queue<int> V3(V1.begin(),V1.end());
效果展示:?
插入元素
V.push(val);
訪問元素
遵循堆的性質,只能訪問堆頂元素
V.top();
獲取元素個數
V.size();
判斷優先隊列是否為空
V.empty();
為空返回 true ;不為空返回 false
刪除堆頂元素
V.pop();
優先隊列的模擬實現
類模板
template<class T, class contain = vector<T>>
class priority_queue
{
public://構造可以不寫,因為可以直接使用vector//函數實現
private:contain x;
}
既然底層是 vector,我們用缺省參數直接實例化出一個 vector 類型的變量就可以作為底層實現了
插入元素
插入元素調用vector的接口就行了,這里由于需要滿足優先隊列的性質(大頂堆),我們還需要在插入之后使用向上調整,保證堆頂(首元素)是最大的
插入元素:
//插入數據
void push(const T& date)
{x.push_back(date);//向上調整Upgrade(size() - 1);
}
向上調整:
//向上調整
void Upgrade(int child)
{//父節點int parent = (child - 1) / 2;//如果孩子節點大于父節點,就向上調整交換(根節點可能也需要調整)while (parent >= 0){//只要進入循環,那么節點下標一定是在合法范圍if (x[child] > x[parent]){//交換swap(x[child], x[parent]);//更新孩子、父節點child = parent;parent = (child - 1) / 2;}elsebreak;}
}
這樣經過向上調整就可以達到下面的效果:
訪問元素
訪問第一個元素即可(堆頂元素)
//訪問元素
T top()
{return x[0];
}
獲取元素個數
//計算元素個數
T size()
{return x.size();
}
判斷優先隊列是否為空
//判斷是否為空
bool empty()
{return x.empty();
}
刪除堆頂元素
這里的刪除調用 vector的尾刪即可。
刪除的方法:先交換堆頂 和 尾部元素,再刪除,再使用向下調整保證大頂堆的性質
//刪除堆頂元素
void pop()
{Eliminate(size() - 1);
}
向下調整:?
//向下調整
void Eliminate(int child)
{//交換堆頂和末尾元素swap(x[child], x[0]);//去尾x.pop_back();//父子節點int parent = 0;child = 2 * parent + 1;//開始調整(子節點不能超過范圍)while (child < x.size()){//如果右節點大于左節點if (x[child] < x[child + 1]){child++;}//如果父節點小于子節點if (x[parent] < x[child]){swap(x[parent], x[child]);//更新parent = child;child = 2 * parent + 1;}elsebreak;}
}
效果展示
void text1_t()
{priority_queue<int> V1;//插入元素V1.push(10);V1.push(15);V1.push(5);V1.push(20);V1.push(0);V1.push(25);//元素個數cout << V1.size() << endl;//訪問堆頂元素cout << V1.top() << endl;//出堆頂元素V1.pop();//訪問堆頂元素cout << V1.top() << endl;//判斷是否為空cout << V1.empty() << endl;
}
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?【霧非霧】期待與你的下次相遇!?