送给大家一句话:
这世上本来就没有童话,微小的获得都需要付出莫大的努力。
– 简蔓 《巧克力色微凉青春》
开始使用优先队列
1 前言
上一篇文章我们实现了stack 与 queue 。接下来我们来认识一个新的容器:优先队列。优先队列具有一些与众不同的特性,也会涉及一种新的事物:仿函数。接下来我们一起来看看吧!
2 优先队列
2.1 什么是优先队列
优先队列是一种容器适配器(容器适配器即将 特定容器类 (vector list 等等)封装作为其底层容器类
),根据严格的弱排序标准,它的第一个元素总是所以元素中最大的!
弱排序标准
1. 两个关键字不能同时“严格弱序”于对方
2. 如果a“严格弱序”于b,且b“严格弱序”于c,则a必须“严格弱序”于c
3. 如果存在两个关键字,任何一个都不“严格弱序”于另一个,则这两个关键字是相等的。
也就是其性质类似与“堆”,可以在堆中随时插入元素,并且只能检索到当前所以元素的最大值或最小值(堆顶元素)。
优先队列 被实现为 容器适配器,queue提供一组特定的成员函数来访问其元素。元素从特定容器的"尾部"弹出,其称为优先队列的顶部。
适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口。
底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:
- empty():检测容器是否为空
- size():返回容器中有效元素个数
- front():返回容器中第一个元素的引用
- push_back():在容器尾部插入元素
- pop_back() : 删除容器尾部元素
标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector。需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。
2.2 使用手册
函数声明 | 接口说明 |
---|---|
priority_queue()/priority_queue(first,last) | 构造一个空的优先级队列 |
empty( ) | 检测优先级队列是否为空,是返回true,否则返回false |
top( ) | 返回优先级队列中最大(最小元素),即堆顶元素 |
push(x) | 在优先级队列中插入元素x |
pop() | 删除优先级队列中最大(最小)元素,即堆顶元素 |
使用起来还是很简单的。
但是注意一下创建的时候需要将模版参数传够
template <class T, class Container = vector<T>,
class Compare = less<typename Container::value_type> > class priority_queue;
- 模版参数 1 是 储存的数据类型
- 模版参数 2 是 底层结构,一般使用vector 或 deque
- 模版参数 3 是 仿函数,提供比较方式(建大堆,还是建小堆),下文我们来学习仿函数
2.3 仿函数
仿函数顾名思义是:类似函数但不是函数。
它是如何实现的呢??? 使用类中的将()操作符重载。就通过这样的类操作符的使用规避了函数指针。先前我们C语言的qsort 函数:
void qsort (void* base, size_t num, size_t size,int (*compar)(const void*,const void*));
其最后一个参数就是函数指针,说实话比较复杂,因为我们在实现函数功能时并不知道会是什么类型,所以就很复杂。而我们通过仿函数,可以使用模版类,然后就自然适配所有的类型:
//比较谁更小的的仿函数
template<class T>
struct less
{
bool operator()(const T& a, const T& b)
{
return a < b;