日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

关于优先队列priority_queue大小根堆、重载操作符的说明

發布時間:2023/12/15 综合教程 29 生活家
生活随笔 收集整理的這篇文章主要介紹了 关于优先队列priority_queue大小根堆、重载操作符的说明 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

關于priority_queue的說明

內部實現

priority_queue 默認情況下,以vector 為底層容器,加上heap(默認max-heap) 處理規則;形成大根堆

priority_queue被歸為 container adapter,也就是對 container 進行封裝一層。

priority_queue 操作規則上是 queue,只允許在尾部加入元素,并從首部取出元素;只不過內部元素具有優先級,優先級高者先出。

priority_queue 的所有元素進出具有一定規則,所以不提供遍歷功能,也不提供迭代器。

疑惑產生

下面為priority_queue 的使用規則,第一個傳入了類型,第二個為容器類型,第三個為比較函數。

template<
    class T,
    class Container = std::vector<T>,
    class Compare = std::less<typename Container::value_type>   //comp默認為less
> class priority_queue;

疑惑關鍵就在于比較函數。

priority_queue 默認形成大根堆,而傳入的comp默認為less

為何傳入一個可將序列順序調為有小到大的函數,建成的堆反而是大頂堆呢?

不知你們有沒有這種感覺?直覺上認為傳入less,建成小頂堆,而傳入greater,建成大頂堆。

源碼解析

std::less()源碼:若__x < __y,則返回true,順序不變,否則,順序發生變化。這個函數名含義與實現效果相一致。


struct less : public binary_function<_Tp, _Tp, bool>
{
    _GLIBCXX14_CONSTEXPR
    bool
    operator()(const _Tp& __x, const _Tp& __y) const
    { return __x < __y; }
};

make_heap中調用的__push_heap源碼:

__push_heap(_RandomAccessIterator __first, _Distance __holeIndex,
            _Distance __topIndex, _Tp __value, _Compare __comp)
    //__holeIndex  新添加節點的索引,即叫做孔洞
    //__topIndex  頂端索引
    //__value   新添加節點的值
    //__comp    比較函數,傳入為less
{
  _Distance __parent = (__holeIndex - 1) / 2;   //找到新節點父節點索引
  while (__holeIndex > __topIndex && __comp(*(__first + __parent), __value)) {
      //若孔洞沒有到達最頂端  &&  父節點的值小于新添加節點的值,則要進行下列操作
      //less中,左邊比右邊小則返回true,與less愿意相同
    *(__first + __holeIndex) = *(__first + __parent);   //將父節點的值放入孔洞
    __holeIndex = __parent; //孔洞的索引編程了原來父節點的索引,往上移動了
    __parent = (__holeIndex - 1) / 2;   //那么孔洞的父節點又要繼續往上找
  }
  *(__first + __holeIndex) = __value; //將新添加節點的值放在找到的位置(孔洞)
}

經過上面源碼分析,傳入的comp就是為了在比較孔洞節點與父節點的大小,若返回true,才會進行交換操作。

所以傳入less,返回true是因為父節點比孔洞節點小,所以要進行交換,則將大的值移動到前面,所以建成的堆為大頂堆。

而傳入greater,返回true是因為父節點比孔洞節點答,所以進行叫喊,則將大的值移動到了后面,所以建成的堆為小頂堆。

所以,就明白了為什么傳入less反而形成了大根堆,而傳入greater則形成了小根堆。

如何使用

#include <queue>
using namespace std;
?
priority_queue<int> que;    //默認定義了最大堆,等同于將第三個參數使用less<int>
priority_queue<int, vector<int>, less<int>> que;  //定義大根堆
?
priority_queue<int, vector<int>, greater<int>> que;  //定義小根堆,VS下需要加入頭文件#include<functional>
?
//測試 priority_queue<int> que;
que.push(3);
que.push(5);
que.push(4);
cout << que.top() << endl;  //5
?
//測試 priority_queue<int, vector<int>, less<int>> que;
que.push(3);
que.push(5);
que.push(4);
cout << que.top() << endl;  //5
?
//測試 priority_queue<int, vector<int>, greater<int>> que;
que.push(3);
que.push(5);
que.push(4);
cout << que.top() << endl;  //3

關于自定義優先級

上面給出了在處理整型等基本數據類型時直接出入less或者greater既可以建立相應的大頂堆或者小頂堆。若是處理結構體呢?如何定義優先級呢?

普通數據類型

基本數據類型的比較函數可以直接使用less<int>或者greater<int>可以滿足建立大根堆或者小根堆。

結構體

對于結構體而言,將結構體放入優先隊列中,比較函數需要建立在針對結構體的具體成員。

參考https://www.cnblogs.com/flipped/p/5691430.html 博客自定義優先級。

自定義優先級的三種方法:

1.<以成員函數重載:

struct Node {   //我們將Node節點放入優先隊列中希望以value進行比較
    Node(int _id, int _value) : id(_id), value(_value){}
    int id;
    int value;
};
?
//大根堆
bool operator < (const Node& a, const Node& b)
{
    return a.value < b.value; //將value的值由大到小排列,形成Node的大根堆
}
?
int main()
{  
    struct Node node1(1, 5);
    struct Node node2(2, 3);
    struct Node node3(3, 4);
    
    priority_queue<Node> que;
    
    que.push(node1);
    que.push(node2);
    que.push(node3);
    
    cout << que.top().value << endl;    //5
}

//小根堆
bool operator < (const Node& a, const Node& b)
{
    return a.value > b.value; //將value的值由小到大排列,形成Node的小根堆
}
?
cout << que.top().value << endl;    //3

我試了 bool operator > (),結果會報二進制“<”: 沒有找到接受“const Node”類型的左操作數的運算符(或沒有可接受的轉換) 錯誤,我想原因應該是這樣吧:priority_queue中默認的比較函數為less,less函數中只用到了 { return __x < __y; },所以重載中若只重載了>,函數找不到<,所以會出現錯誤。

struct less : public binary_function<_Tp, _Tp, bool>
{
    _GLIBCXX14_CONSTEXPR
    bool
    operator()(const _Tp& __x, const _Tp& __y) const
    { return __x < __y; }
};
 

2.自定義比較函數:


struct cmp{
?
    bool operator ()(const Node& a, const Node& b)
    {
        return a.value < b.value;//將value的值由大到小排列,形成Node的大根堆
    }
};
priority_queue<Node, vector<Node>, cmp>q;
cout << que.top().value << endl;    //5

struct cmp{
?
    bool operator ()(const Node& a, const Node& b)
    {
        return a.value > b.value;//將value的值由小到大排列,形成Node的小根堆
    }
};
priority_queue<Node, vector<Node>, cmp>q;
cout << que.top().value << endl;    //3

上述在傳入用戶自定義的比較函數,那么在建堆過程中使用的comp函數即為我們自定義的cmp,這樣分析同上。

3.<以類成員函數重載

struct Node {    //我們將Node節點放入優先隊列中希望以value進行比較
    Node(int _id, int _value) : id(_id), value(_value){}
    int id;
    int value;
    //大根堆
    bool operator < (const Node& b) const    //注意,此處若沒有const則會報錯
    {
        return value < b.value; //將value的值由大到小排列,形成Node的大根堆
    }

};

cout << que.top().value << endl;  //5

4.<以友元函數重載

struct Node{
    int id;
    int value;
    friend bool operator<(const Node& a,const Node& b){
        return a.value<b.value;  //按value從大到小排列
    }
};
priority_queue<Node>q;    

剛開始不太明白友元操作符重載函數,先了解下重載友元

重載類型

參考:https://www.cnblogs.com/Mayfly-nymph/p/9034936.html

綜合,在C++中,操作符重載實現通過類成員函數全局函數(非類成員函數)友元函數(不是類成員卻能夠訪問類的所有成員 )。

上面對<重載使用到類全局函數、友元函數、類成員函數。

使用友元函數重載有兩個優點:和普通函數重載相比,它能夠訪問類的非公有成員將雙目運算符重載為友元函數,這樣就可以使用交換律

交換律可以理解成復數加法中,以成員函數的形式重載 +,只能計算c + 15.6,不能計算28.23 + c,這是不對稱的 。

何為友元

友元函數

類的友元函數是定義在類外部,但有權訪問類的所有私有(private)成員和保護(protected)成員。

盡管友元函數的原型有在類的定義中出現過,但是友元函數并不是成員函數。

友元可以是一個函數,該函數被稱為友元函數

友元也可以是一個,該類被稱為友元類,在這種情況下,整個類及其所有成員都是友元。

如果要聲明函數為一個類的友元,需要在類定義中該函數原型前使用關鍵字 friend。如下設置友元函數:


class Box
{
   double width;
public:
   double length;
   friend void printWidth( Box box );
   void setWidth( double wid );
};

聲明類 Usb 的所有成員函數作為類 Phone 的友元,需要在類 Phone 的定義中放置如下聲明:

class Usb
{
private:
    int size;
    friend class Phone;  //聲明 Phone為友元類
};
class Phone
{
public:
    Usb usb;
    void setPhone() 
{ usb.size += 1000; //因Phone是Usb的友元類,故此處可以訪問其私有成員 } };

總結

以上是生活随笔為你收集整理的关于优先队列priority_queue大小根堆、重载操作符的说明的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。