標籤:ble sort 優先 style contain template 繼承 effective body
前言
最近在刷演算法題,常常需要自訂比較函數作為作為函數對象送入 stl 中,遇到了下面的問題:
泛型演算法 sort() 的比較函數是這麼寫:
//sort() 實現元素間關係為遞增的比較函數struct cmp{ bool operator () (const T& a, const T& b) const { return a.x < b.x; }};//或者這樣bool operator < (const T& a, const T& b) const { return a.x < b.x; }
而優先隊列裡的比較函數是這麼寫:
//priority_queue 實現元素間關係為遞增(實現最小堆)的比較函數struct cmp {
DataType x; bool operator () (const T& a, const T& b) const { return a.x > b.x; }}//或者這樣struct T { Type_2 x; friend bool operator < (const T&a, const T& b) const { return a.x > b.x; }}
同樣是實現元素的遞增關係即 “前驅 < 後繼”,為什麼一個是 “a.x < b.x”,一個是"a.x > b.x" 呢?
分析 sort()
先拿 sort() 進行分析,排序類的泛型演算法很多,它們的 cmp 寫法是相似的,這裡先看一份冒泡排序實現遞增關係如 <1, 2, 3 ..> 的寫法,法一:當傳入的 class compare 是cmp 是這個函數對象時,它會重載 () 使得 *current 、*next 進行 “<” 比較 ,如果兩個元素的關係不滿足 “<” ,則交換。
#include <iostream> template <class BidirectionalIt, class Compare> //雙向迭代器void bubble_sort(BidirectionalIt first, BidirectionalIt last, Compare comp) { for (; first != last; --last) for (BidirectionalIt current = first, next = first; ++next != last; ++current) if (!comp(*current, *next)) std::swap(*current, *next); } }//可以這麼寫struct cmp { bool oprator ()(int a, int b) { return a < b; }};//或者這麼寫bool cmp (int a, int b){ return a < b;}
一言以蔽之,就是元素將按照 cmp 中定義的邏輯關係進行排列。
雖然 sort() 是用 快排+堆排+插入實現的而並非冒泡,但是原理是一樣。
分析 priority_queue
現在我們再來看看優先隊列。
STL優先隊列模板:priority_queue<Type, Container, Functional> ,參數分別對應的是 元素類型、容器類型、比較函數。
注意,優先隊列不是 STL 的容器,而是由 底層容器 vector/deque 實現,預設情況下它利用 MAX-HEAP 實現,而 MAX-HEAP 是 vector 實現的完全二叉樹,這樣的東西稱為適配器 (adapter)。
在優先隊列中預設優先順序高的先出隊列,具體操作是使用函數對象 less<> 重載了 “<”,所以這裡優先順序的高低是由 “<” 確定的,所以當我們按下面的形式寫:
priority_queue<int> pQueue;
通過操作符 < 知道元素關鍵字大的優先順序高,此時是最大堆。若每次從堆頂取一個元素,最後得到一個序列,那麼序列中元素的關係是遞減的。
我們想要讓元素的關係是遞增該怎麼做?
第一種辦法是使用與 less<>作用相反的函數對象 greater<> 。
第二種辦法就是使用自訂比較函數,通過它實現對自訂資料類型的優先順序的定義。
2-1. 通過重載 operator < 操作符以比較元素的優先順序
struct T{ DataType key; friend bool operator < (const T& a, const T& b) { return a.key > b.key; }};
邏輯是這樣:假設 a.key > b.key 為 TRUE ,由於預設是最大堆,此時優先隊列會認為 a < b ,即 b 的優先順序比 a 高,所以 b會被先出隊,這樣就實現了關鍵字小的元素先出隊。最後輸出的序列是按關鍵字遞增的。
2-2. 通過重載 operator () 以定義元素的優先順序。
struct cmp{ bool operator () (const Type& a, const Type& b) const { return a.x > b.x; }};
邏輯和 2-1 是一樣的。這裡重載了 () 的目的是使得 cmp 成為像 less<> 一樣的函數對象(實質上是繼承了 less<>) ,作為新的比較比較級的規則以 priority_queue 第三個參數的形式傳入 priority_queue 中。
總結
優先隊列的“比較級”概念與”由MAX-HEAP實現“這一特性,使得自訂比較函數中需要這麼寫: "a.x>b.x" 。
延伸閱讀
永遠讓比較函數對相等的值返回false(來自Effective C++)
C++中的 sort 使用自訂比較函數的具體運行過程是怎樣的呢?
STL 優先隊列的自訂比較函數與 sort() 等泛型演算法的自訂比較函數的區別