C++初阶----priority_queue模拟实现+仿函数
Posted 4nc414g0n
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++初阶----priority_queue模拟实现+仿函数相关的知识,希望对你有一定的参考价值。
priority_queue模拟实现+仿函数
1)优先队列
1. 介绍
- 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素
总是它所包含的元素中最大的
- 此上下文类似于堆,在堆中可以随时插入元素,并且
只能检索最大堆元素(优先队列中位于顶部的元素)
- 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部
- 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:
empty()
:检测容器是否为空
size()
:返回容器中有效元素个数
front()
:返回容器中第一个元素的引用
push_back()
:在容器尾部插入元素
pop_back()
:删除容器尾部元素- 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector
- 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作
2. 操作
函数 | 功能 |
---|---|
empty | 测试容器是否为空 |
size | 返回大小 |
top | 访问顶部元素 |
pop | 插入元素 |
push | 移除顶部元素 |
3. 模拟实现
需要回忆的知识:堆向上调整算法和堆向下调整算法
①结构
包含一个Container类型的成员变量
/*template <class T, class Container = std::vector<T>, class Compare = std::less<typename Container::value_type> >*/ //vector里将T typedef为了type_value template <class T, class Container = std::vector<T>> class priority_queue public: priority_queue()//默认构造 template <class InputIterator> priority_queue(InputIterator first, InputIterator last)//迭代器区间构造 private: Container c; //Compare comp; ;
②构造函数
默认构造和迭代器区间构造
priority_queue()//默认构造 template <class InputIterator> priority_queue(InputIterator first, InputIterator last)//迭代器区间构造 while (first != last)//拷贝 c.push_back(*first); first++; for (int i = (c.size() - 1 - 1) / 2; i >= 0; i--) //建堆(向下调整算法的前提是左右子树是堆)i从最后一个非叶子节点开始 AdjustDown(i);
③成员函数(empty,size,top)
bool empty() const return c.empty(); size_t size() const size_t count = 0; for (auto e : c) count++; return count; const T& top() const return c[0];
④成员函数(push pop)
- pop是删除堆顶元素(优先级最高的)(库里默认是大堆(less))要让小数优先级更高可以传一个仿函数
参考topK算法
每次pop的时候先把堆顶和最后一个元素交换,在用队向下调整算法调整堆(子树都满足堆)- push就是先push_back到尾部,再向上调整(向上调整算法只会改变一条路径)
void push(const T& x) c.push_back(x); AdjustUp(c.size() - 1);//向上调整算法只会改变一条路径 void pop() //参考topK算法 std::swap(c[0], c[size() - 1]); c.pop_back();//删除尾部的是交换后的也是优先级最高的(大堆为例) AdjustDown(0);//除了堆顶,其他的子树都保证是堆
2)仿函数
C++中的仿函数效果类似于C语言中使用函数指针和回调函数
具体参考:C语言quick sort的实现(quicksort函数的最后一个参数是函数指针
)
仿函数是模板函数,也可以叫做
函数对象
,本质就是其实现就是类中实现一个operator()
,其速度比一般函数要慢
在priority_queue里用的仿函数less来做模板的最后一个缺省参数template <class T, class Container = vector<T>, class Compare = less<typename Container::value_type> > class priority_queue;
它在这里的作用就是修改向上/向下调整算法的父子节点的比较条件 (大于(
小堆
),小于(大堆
))
下面是具体实现
:template <class T> struct Less //大堆 堆顶最大 //public: bool operator()(const T& x,const T& y) return x < y; ; template <class T> struct Greater //小队 堆最小 //public: bool operator()(const T& x, const T& y) return x > y; ;
所以:
我们在模拟实现的时候把priority_queue的模板改为:
template <class T, class Container = std::vector<T>, class Compare = std::less<typename Container::value_type> >
多定义一个成员变量
Compare comp
用于算中的比较在堆向上/向下调整算法中将所有节点之间的比较改为
comp(c[parent], c[child])
或comp(c[child], c[child+1])
仿函数(变异版本)
例子
:(通过仿函数控制比较方式)
有一个Date类 (已经重载了输出<<和比较<
)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); friend ostream& operator<<(ostream& _cout, const Date& d); // friend class PDateLess; private: int _year; int _month; int _day; ; ostream& operator<<(ostream& _cout, const Date& d) _cout << d._year << "-" << d._month << "-" << d._day<<endl; return _cout;
而实例化priority_queue的时候将Date*做为类型传入的(
默认使用的是fonctional.h里的less仿函数
)priority_queue<Date*, vector<Date*>> pq; pq.push(new Date(2023, 11, 24)); pq.push(new Date(2021, 10, 24)); pq.push(new Date(2021, 12, 24)); pq.push(new Date(2022, 1, 24)); cout<< (*pq.top()) << endl;
输出的是
2022-1-24而不是2023-11-24
,因为优先队列里存的是地址
,less函数对象比较的也是地址
,优先级最高的是地址最大的即最后new的对象Date(2022, 1, 24)
所以这里需要我们自己实现
一个针对这种情况的PDataLess函数对象:class PDateLess public: bool operator()(const Date* p1, const Date* p2) return *p1 < *p2; ;
同时在Date类里添加PDateLess友元:
friend class PDateLess;
实例化priority_queue时传入的参数应该为:priority_queue<Date*, vector<Date*>, PDateLess> pq;
3) typename
???? T
???特殊情况(hash 红黑树部分)
以上是关于C++初阶----priority_queue模拟实现+仿函数的主要内容,如果未能解决你的问题,请参考以下文章
C++ 初阶优先级队列(Priority_Queue)底层框架模拟实现
C++初阶第十二篇—stack和queue(stack和queue的常见接口的用法与介绍+priority_queue+容器适配器+仿函数+模拟实现)
C++初阶:STL —— stack and queuestack/queue的介绍及使用 | stack/queue/priority_queue的深度剖析及模拟实现 | 适配器模式 | 仿函数
C++初阶:STL —— stack and queuestack/queue的介绍及使用 | stack/queue/priority_queue的深度剖析及模拟实现 | 适配器模式 | 仿函数
C++初阶:STL —— stack and queuestack/queue的介绍及使用 | stack/queue/priority_queue的深度剖析及模拟实现 | 适配器模式 | 仿函数