目錄?🌠作者:@阿亮joy.
天津ssl適用于網(wǎng)站、小程序/APP、API接口等需要進行數(shù)據(jù)傳輸應用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)建站的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18980820575(備注:SSL證書合作)期待與您的合作!
🎆專欄:《吃透西嘎嘎》
🎇座右銘:每個優(yōu)秀的人都有一段沉默的時光,那段時光是付出了很多努力卻得不到結果的日子,我們把它叫做扎根
👉priority_queue 的使用👈
- 優(yōu)先隊列是一種容器適配器,根據(jù)嚴格的弱排序標準,它的第一個元素總是它所包含的元素中大的。
- 此上下文類似于堆,在堆中可以隨時插入元素,并且只能檢索大堆元素(優(yōu)先隊列中位于頂部的元素)。
- 優(yōu)先隊列被實現(xiàn)為容器適配器,容器適配器即將特定容器類封裝作為其底層容器類.priority_queue 提供一組特定的成員函數(shù)來訪問其元素,元素從特定容器的尾部彈出,其稱為優(yōu)先隊列的頂部。
- 底層容器可以是任何標準容器類模板,也可以是其他特定設計的容器類。容器應該可以通過隨機訪問迭代器訪問,并支持以下操作:
- empty():檢測容器是否為空
- size():返回容器中有效元素個數(shù)
- front():返回容器中第一個元素的引用
- push_back():在容器尾部插入元素
- pop_back():刪除容器尾部元素
- 標準容器類 vector 和 deque 滿足這些需求。默認情況下,如果沒有為特定的 priority_queue 類實例化指定容器類,則使用 vector。
- 需要支持隨機訪問迭代器,以便始終在內部保持堆結構。容器適配器通過在需要時自動調用算法函數(shù) make_heap、push_heap 和 pop_heap 來自動完成此操作。
優(yōu)先級隊列默認使用 vector 作為其底層存儲數(shù)據(jù)的容器。在 vector 上又使用了向上調整算法和向下調整算法將 vector 中元素構造成堆的結構,因此 priority_queue 就是堆。所有需要用到堆的地方,都可以考慮使用priority_queue。注意:默認情況下 priority_queue 是大堆。
函數(shù)聲明 | 接口說明 |
---|---|
priority_queue() | 構造一個空的優(yōu)先級隊列 |
priority_queue(first, last) | 迭代器區(qū)間構造優(yōu)先級隊列 |
empty( ) | 檢測優(yōu)先級隊列是否為空,是返回 true,否則返回 false |
top( ) | 返回優(yōu)先級隊列中大元素(最小元素),即堆頂元素 |
push(x) | 在優(yōu)先級隊列中插入元素 x |
pop() | 刪除優(yōu)先級隊列中大元素(最小元素),即堆頂元素 |
size() | 返回優(yōu)先級隊列中元素的個數(shù) |
代碼示例:
#include// 優(yōu)先級隊列priority的頭文件
#includeusing namespace std;
#include// greater算法的頭文件
int main()
{// 默認情況下,創(chuàng)建的是大堆,其底層是按照小于號比較的
priority_queuepq1;
pq1.push(3);
pq1.push(2);
pq1.push(8);
pq1.push(5);
pq1.push(1);
while (!pq1.empty())
{cout<< pq1.top()<< " ";
pq1.pop();
}
cout<< endl;
// 如果要創(chuàng)建小堆,將第三個模板參數(shù)換成greater比較方式,其底層是按照大于號比較的
priority_queue, greater>pq2;
pq2.push(3);
pq2.push(2);
pq2.push(8);
pq2.push(5);
pq2.push(1);
while (!pq2.empty())
{cout<< pq2.top()<< " ";
pq2.pop();
}
cout<< endl;
return 0;
}
如果在priority_queue中放自定義類型的數(shù)據(jù),用戶需要在自定義類型中提供 >或者< 的重載。
#include// 優(yōu)先級隊列priority的頭文件
#include// greater算法的頭文件
#includeusing namespace std;
class Date
{public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
bool operator<(const Date& d)const
{return (_year< d._year) ||
(_year == d._year && _month< d._month) ||
(_year == d._year && _month == d._month && _day< d._day);
}
bool operator>(const Date& d)const
{return (_year >d._year) ||
(_year == d._year && _month >d._month) ||
(_year == d._year && _month == d._month && _day >d._day);
}
friend ostream& operator<<(ostream& _cout, const Date& d)
{_cout<< d._year<< "-"<< d._month<< "-"<< d._day;
return _cout;
}
private:
int _year;
int _month;
int _day;
};
void TestPriorityQueue()
{// 大堆,需要用戶在自定義類型中提供<的重載
priority_queueq1;
q1.push(Date(2018, 10, 29));
q1.push(Date(2018, 10, 28));
q1.push(Date(2018, 10, 30));
while (!q1.empty())
{cout<< q1.top()<< " ";
q1.pop();
}
cout<< endl;
// 如果要創(chuàng)建小堆,需要用戶提供>的重載
priority_queue, greater>q2;
q2.push(Date(2018, 10, 29));
q2.push(Date(2018, 10, 28));
q2.push(Date(2018, 10, 30));
while (!q2.empty())
{cout<< q2.top()<< " ";
q2.pop();
}
cout<< endl;
}
int main()
{TestPriorityQueue();
return 0;
}
優(yōu)先級的隊列使用起來沒有什么難的,接下來我們來做一道題目,順便回顧一下堆。
數(shù)組中的第K個大元素
給定整數(shù)數(shù)組 nums 和整數(shù) k,請返回數(shù)組中第 k 個大的元素。
請注意,你需要找的是數(shù)組排序后的第 k 個大的元素,而不是第 k 個不同的元素。
你必須設計并實現(xiàn)時間復雜度為 O(n) 的算法解決此問題。
解決這道題有兩種思路,第一種就是用數(shù)組的元素建大堆,然后 pop 掉 k - 1 個元素,此時堆頂?shù)脑鼐褪堑?k 大的數(shù)。這種思路的時間復雜度為O(N + k * lgN)
,當 N 很大時,就會需要非常多的空間。這時候,我們可以考慮第二種思路就是用前 k 個數(shù)建只包含 k 個數(shù)的小堆,再遍歷數(shù)組剩下的元素,比堆頂元素大則替換掉堆頂元素,再向下調整堆。這種思路的時間復雜度為O(k + (N - k) * lgk)
注:頂元素不允許修改,因為堆頂元素修改可以會破壞堆的特性。
class Solution
{public:
int findKthLargest(vector& nums, int k)
{// 建n個數(shù)的大堆
priority_queuepq(nums.begin(), nums.end());
// pop掉前k-1個大的數(shù)
while(--k) pq.pop();
return pq.top();
}
};
class Solution
{public:
int findKthLargest(vector& nums, int k)
{// 建k個數(shù)的小堆,從下標k開始遍歷數(shù)組
// 如果num[i]大于堆頂?shù)臄?shù)據(jù),那么nums[i]入堆
// 遍歷結束,堆頂?shù)臄?shù)就是第k大的數(shù)
priority_queue, greater>pq(nums.begin(), nums.begin() + k);
for(size_t i = k; i< nums.size(); ++i)
{if(nums[i] >pq.top())
{pq.pop();
pq.push(nums[i]);
}
}
return pq.top();
}
};
👉仿函數(shù)👈仿函數(shù)也稱為函數(shù)對象,它是具有operator()
的類對象,可以想函數(shù)一樣來使用它。
#includeusing namespace std;
// 仿函數(shù)/函數(shù)對象
templateclass Less
{public:
templatebool operator()(const T& left, const T& right)
{return left< right;
}
};
templateclass Greater
{public:
templatebool operator()(const T& left, const T& right)
{return left >right;
}
};
int main()
{// 仿函數(shù)用起來就像是函數(shù)
// 有名對象
LesslessFunc;
cout<< lessFunc(1, 2)<< endl;
// lessFunc(1, 2)<==>lessFunc.operator()(1, 2);
// 匿名對象
cout<< Greater()(1, 2)<< endl;
return 0;
}
這樣一看,好像仿函數(shù)也沒有特別大的又是,和函數(shù)指針也沒有什么大的區(qū)別嘛!其實不然,函數(shù)指針需要寫函數(shù)的參數(shù)和返回值類型。所以仿函數(shù)用起來還是比函數(shù)指針舒服的。
#includeusing namespace std;
// 仿函數(shù)/函數(shù)對象
templateclass Less
{public:
templatebool operator()(const T& left, const T& right)
{return left< right;
}
};
templateclass Greater
{public:
templatebool operator()(const T& left, const T& right)
{return left >right;
}
};
templatevoid BubbleSort(T* a, int n, Compare com)
{for (int i = 0; i< n - 1; ++i)
{int exchange = 0;
for (int j = 1; j< n - i; ++j)
{ // if (a[i]< a[i - 1])
if (com(a[j], a[j - 1]))
{ swap(a[j], a[j - 1]);
exchange = 1;
}
}
if (exchange == 0)
break;
}
}
int main()
{LesslessFunc;
int arr[] = {2, 7,3, 1, 5, 9 ,10,20 };
BubbleSort(arr, sizeof(arr) / sizeof(arr[0]), lessFunc);
for (auto e : arr)
cout<< e<< " ";
cout<< endl;
BubbleSort(arr, sizeof(arr) / sizeof(arr[0]), Greater());
for (auto e : arr)
cout<< e<< " ";
cout<< endl;
return 0;
}
注:沒有成員變量的類的大小是 1 個字節(jié),所以傳參時加不加引用都沒有關系。如果參數(shù)用 const 修飾了,那么仿函數(shù)也要用 const 來修飾。
如果庫提供的仿函數(shù)不符合我們的需求,我們可以自己寫!
#include// 優(yōu)先級隊列priority的頭文件
#include// greater算法的頭文件
#includeusing namespace std;
class Date
{public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
bool operator<(const Date& d)const
{return (_year< d._year) ||
(_year == d._year && _month< d._month) ||
(_year == d._year && _month == d._month && _day< d._day);
}
bool operator>(const Date& d)const
{return (_year >d._year) ||
(_year == d._year && _month >d._month) ||
(_year == d._year && _month == d._month && _day >d._day);
}
friend ostream& operator<<(ostream& _cout, const Date& d)
{_cout<< d._year<< "-"<< d._month<< "-"<< d._day;
return _cout;
}
private:
int _year;
int _month;
int _day;
};
struct PDateLess
{bool operator()(const Date* d1, const Date* d2)
{return *d1< *d2;
}
};
struct PDateGreater
{bool operator()(const Date* d1, const Date* d2)
{return *d1 >*d2;
}
};
void TestPriorityQueue()
{priority_queue, PDateLess>q3;
q3.push(new Date(2018, 10, 29));
q3.push(new Date(2018, 10, 28));
q3.push(new Date(2018, 10, 30));
while (!q3.empty())
{cout<< *q3.top()<< " ";
q3.pop();
}
cout<< endl;
priority_queue, PDateGreater>q4;
q4.push(new Date(2018, 10, 29));
q4.push(new Date(2018, 10, 28));
q4.push(new Date(2018, 10, 30));
while (!q4.empty())
{cout<< *q4.top()<< " ";
q4.pop();
}
cout<< endl;
}
int main()
{TestPriorityQueue();
return 0;
}
現(xiàn)在學習到的仿函數(shù)還只是冰山一角,以后還會有更多的仿函數(shù)要學?。。?/p>👉priority_queue 的模擬實現(xiàn)👈
// PriorityQueue.h
#pragma once
namespace Joy
{// 仿函數(shù)
templatestruct less
{bool operator()(const T& left, const T& right)
{ return left< right;
}
};
templatestruct greater
{bool operator()(const T& left, const T& right)
{ return left >right;
}
};
template, class Compare = less>class priority_queue
{public:
priority_queue()
: _con()
{}
templatepriority_queue(InputIterator first, InputIterator last)
: _con(first, last)
{ // 注意:這里要使用int!如果使用size_t,如果只有一個元素會出現(xiàn)Bug
int size = _con.size();
for (int i = (size - 2) / 2; i >= 0; --i)
adjust_down(i);
}
void push(const T& val)
{ _con.push_back(val);
adjust_up(_con.size() - 1);
}
void pop()
{ //swap(_con[0], _con[_con.size() - 1]);
swap(_con.front(), _con.back());
_con.pop_back();
adjust_down(0);
}
bool empty() const
{ return _con.empty();
}
size_t size() const
{ return _con.size();
}
const T& top() const
{ assert(!empty());
return _con[0];
}
private:
Container _con; // 底層容器
// 向上調整算法
void adjust_up(size_t child)
{ size_t parent = (child - 1) / 2;
while (child >0)
{
//if(_con[parent]< _con[child])
if (Compare()(_con[parent], _con[child])) // 匿名對象
{swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else
break;
}
}
void adjust_down(size_t parent)
{ size_t child = 2 * parent + 1;
size_t size = _con.size();
while (child< size)
{ // 找出較大的孩子
if (child + 1< size && Compare()(_con[child], _con[child + 1]))
++child;
if (Compare()(_con[parent], _con[child]))
{swap(_con[parent], _con[child]);
parent = child;
child = 2 * parent + 1;
}
else
break;
}
}
};
}
// Test.cpp
#include#includeusing namespace std;
#include "PriorityQueue.h"
int main()
{//priority_queuepq; 默認是大堆
//Joy::priority_queuepq;
Joy::priority_queue, greater>pq;
pq.push(3);
pq.push(2);
pq.push(8);
pq.push(5);
pq.push(1);
while (!pq.empty())
{cout<< pq.top()<< " ";
pq.pop();
}
cout<< endl;
return 0;
}
優(yōu)先級隊列的實現(xiàn)就不講解了,和用C語言大的區(qū)別就是不需要自己造輪子了,直接使用 vector 適配出優(yōu)先級隊列。還有就是使用了仿函數(shù),可以根據(jù)傳入的仿函數(shù)生成大堆還是小堆。如果還有相關的實現(xiàn)細節(jié)不了解的話,可以看這篇文章:堆的實現(xiàn)!
👉反向迭代器👈方向迭代器其實也是一種適配器,它可以適配出各種容器的反向迭代器,其主要的是將正向迭代器進行了封裝,反向迭代器 ++ 就復用正向迭代器的 - -,反向迭代器 - - 就復用正向迭代器的 ++。
#pragma once
templateclass ReverseInterator
{public:
typedef ReverseInteratorSelf;
ReverseInterator(Interator it)
: _it(it)
{}
Self& operator++()
{--_it;
return *this;
}
Self operator++(int)
{auto tmp = _it;
--_it;
return tmp;
}
Self& operator--()
{++_it;
return *this;
}
Self operator--(int)
{auto tmp = _it;
++_it;
return tmp;
}
Ref operator*()
{auto tmp = _it;
return *(--tmp);
}
// 返回當前對象的地址
Ptr operator->()
{return &(operator*());
}
bool operator!=(const Self& s) const
{return _it != s._it;
}
bool operator==(const Self& s) const
{return _it == s._it;
}
private:
Interator _it; // 對正向迭代器進行封裝
};
因為rbegin()
是用end()
來構造的,rend()
使用begin()
來構造的,所以反向迭代器的解引用需要創(chuàng)建一個臨時對象tmp
,再返回*(--tmp)
。
測試反向迭代器
注:以上的反向迭代器是用我們自己模擬實現(xiàn)的 list 來測試的!
👉總結👈本篇博客主要講解了什么是優(yōu)先級隊列、優(yōu)先級隊列的使用和模擬實現(xiàn)、仿函數(shù)以及反向迭代器的實現(xiàn)等等。那么以上就是本篇博客的全部內容了,如果大家覺得有收獲的話,可以點個三連支持一下!謝謝大家!💖💝??
你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧