探究STL 優先隊列的自定義比較方式與 sort() 等泛型算法的自定義比較方式的區別

前言html


  

  最近在刷算法題,經常須要自定義比較函數做爲做爲函數對象送入 stl 中,遇到了下面的問題:ios

   泛型算法 sort() 實現從小到大的遞增關係是這麼寫: 算法

//sort 函數的模板聲明 // 能夠看出,排序要求容器支持隨機訪問迭代器,相似於數組的那種下標偏移訪問 // 這裏 _Compare 是類型, __comp 是實例,調用 sort 須要傳入的就是 __comp 實例
template <class _RandomAccessIter, class _Compare> inline void sort(_RandomAccessIter __first, _RandomAccessIter __last, _Compare __comp) //利用priority_queue 實現元素間關係爲遞增(實現最小堆) 的方法以下: //自定義比較函數,排序時傳入函數指針,編譯器會進行類型推導作模板特化 
bool cmp (const T& a, const T& b) { return a.x < b.x; } //對於自定義結構體,可重載 < 運算符,排序時會默認調用 operator < 
struct T{ friend bool operator < (const T& a) { return this.x < a.x; } };

 

 

  而優先隊列裏是這麼寫:數組

// priority_queue 實現使用的默認比較是 operator< ,是最大堆數據結構,即隊列頭元素值最大
template <class _Tp, class _Sequence __STL_DEPENDENT_DEFAULT_TMPL(vector<_Tp>), class _Compare __STL_DEPENDENT_DEFAULT_TMPL(less<typename _Sequence::value_type>) class priority_queue;  // 注意點:若是傳入本身的仿函數,那麼第二個存儲類型也要傳入
//利用priority_queue 實現元素間關係爲遞增(實現最小堆) 的方法以下: //仿函數 struct cmp {   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) { return a.x > b.x; } }

 

  一樣是實現元素的遞增關係即 「前一元素的x份量 < 後一元素的x份量」,爲何前一個是 「a.x < b.x」,然後一個是"a.x > b.x" 呢?數據結構

 

分析 sort() less


 

  先拿 sort() 進行分析,排序類的泛型算法不少,它們的 cmp 寫法是類似的,這裏先看一份冒泡排序實現遞增關係如 <1, 2, 3 ..> 的寫法,傳入的 cmp 將特化爲 Compare 實例 comp ,使得容器中的相鄰元素按 cmp 中的邏輯進行排序。dom

#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); } }
bool cmp (int a, int b){ return a < b; } 

  一言以蔽之,就是元素將按照 cmp 中定義的邏輯關係進行排列。函數

  雖然 sort() 是用「快排+堆排+插入」實現的而並不是冒泡,可是原理是同樣的。this

 

分析 priority_queue spa


  

  如今咱們再來看看優先隊列。

  STL優先隊列模板:priority_queue<Type, Container, Functional> ,參數分別對應的是「元素類型」、「容器類型」、「比較函數」。

  注意,優先隊列不是 STL 的容器,而是由 底層容器 vector/deque 實現的模板類,缺省狀況下它利用 MAX-HEAP 實現,而 MAX-HEAP 是 vector 實現的徹底二叉樹,這樣的東西稱爲適配器 (adapter)。

  在優先隊列中默認優先級高的元素先出隊列,具體操做是使用函數對象 less<> 重載了 「<」,因此這裏優先級的高低是由 「<」 肯定的,因此當咱們按下面的形式寫:

priority_queue<int> pQueue;

  經過操做符 < 知道元素關鍵字大的優先級高,此時是最大堆。若每次從堆頂取一個元素,最後獲得一個序列,那麼序列中元素的關係是遞減的。

  咱們想要讓元素的關係是遞增該怎麼作?

   第一種辦法:使用與 less<>做用相反的函數對象 greater<> 。

    ex:以下

priority_queue<int, vector<int>, greater<int> >qi2;

  

  第二種辦法:在自定義結構體中重載  < ,經過它實現對自定義數據類型的優先級的定義。

    ex:經過重載 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會被先出隊,這樣就實現了關鍵字小的元素先出隊。最後輸出的序列是按關鍵字遞增的。

    但此時不能像基本類型這樣聲明 :priority_queue<T, vector<T>, greater<T> > (注意空格);

    而是隻能這樣聲明:priority_queue<T>

    緣由是 greater<T> 沒有定義,若是想用這種方法定義,則使用方法3。

    注意,這種辦法有缺點,若是有兩個模板容器對同一個自定義數據結構須要不一樣的比較器,那麼辦法就不如函數對象適用了。

  

  第三種辦法:經過函數對象以定義元素的優先級。

  ex:以下

struct T { int x; T(int xx) : x(xx) {} }; struct cmp{ bool operator () (const T& a, const T& b) const
  {
return a.x > b.x; } };

    邏輯和 2 是同樣的。這裏重載了 () 的目的是使得 cmp 成爲像 less<> 同樣的函數對象,做爲新的比較比較級的規則以 priority_queue 第三個參數的形式傳入 priority_queue 中。

   

總結


 

  優先隊列的「比較級」概念與」由MAX-HEAP實現「這一特性,使得自定義比較函數中須要這麼寫: "a.x>b.x" 。

 

延伸閱讀


 

  永遠讓比較函數對相等的值返回false(來自Effective C++)

  C++中的 sort 使用自定義比較函數的具體運行過程是怎樣的呢?

  C++自定義比較:仿函數、函數與重載運算符 

相關文章
相關標籤/搜索