数据结构与函数使用1:优先队列Priority Queues
本文目的
简单的介绍一下优先队列是什么,有什么用,在写算法面试题的时候如何使用
前置知识点
- 堆排序算法
- 仿函数:仿函数 function objects
- 类与模板
- 运算符重载
- C++基本语法基础
优先队列Priority Queues简介
实现实时加入或删除堆顶元素,堆顶元素总是优先级最高的
这里的优先级可以是数的最大,向量的长度的最长等等。
优先队列的算法原理
就是堆排序算法,加入元素和删除元素的时间复杂度都是O(nlogn),本栏目只介绍优先队列本身的简介和使用,不介绍详细算法。
优先队列的使用
1. 头文件定义
#include<queue>
2. 容器声明
2.1 相关模板定义
查看头文件里面的priority_queue定义是这样的,使用了模板(会自动识别元素内容)
* @tparam _Tp Type of element.
* @tparam _Sequence Type of underlying sequence, defaults to vector<_Tp>.
* @tparam _Compare Comparison function object type, defaults to
* less<_Sequence::value_type>.
template<typename _Tp, typename _Sequence = vector<_Tp>,
typename _Compare = less<typename _Sequence::value_type> >
class priority_queue
其中function object type是仿函数(也叫函数对象)
模板的定义参数如下
- 参数1:Typename _Tp,元素类型
- 参数2:Typename _Sequence,容器,默认是vector
- 参数3:Typename _Compare,一个比较器用于定义优先级(需要是仿函数),缺省使用less(也就是<运算符)
2.2 默认优先方式的容器声明(大根堆)
模板的后面两个参数都是可以缺省的,也就是只要填写参数类型type即可
#include<queue>
using namespace std;
priority_queue<type> q; //type是指容器类型。比如int,char,string,pair<int,int>等。
priority_queue<int> qint;
2.3 自定义优先级的容器声明
2.3.1仿函数方法
由于模板的第三个参数才是比较器,所以第二个参数也不能省略,一般写vector<type>
#include<queue>
std::priority_queue<type, vector<type>, cmp> q;
//type是指容器类型。比如int,char,string,pair<int,int>等。
//cmp是比较器,这个比较器后面说怎么写
std::priority_queue<int, vector<int>, greater<int>> minq;//例子:小根堆
模板参数cmp并不是一个函数(不是函数指针类型),而是一个仿函数。
只是优先队列在比较的时候调用了这个cmp的()运算符而已
注意:a<b其实就是调用了less(a,b),这也是为什么运算符重载也可以修改自定义优先级
所以需要填进去的是一个自定义了()运算符的结构体或者类,这种使用方式一般叫做仿函数
相关内容参考《C++标准库 自修教程与参考手册》8.1.1仿函数可当作排序准则
程序员经常需要把某些class object以已排序的形式置于容器中,当需要以特定的排序规则来排序的时候,仿函数可以派上用场。具体的方式是编写一个类的()运算符重载
举个例子:using namespace std; class myless{ public: bool operator() (const int& lhs, const int& rhs) const{ return lhs < rhs; } }; priority_queue<int,vector<int>,myless> q;
对于优先队列,这里认为右边值是优先值(和sort函数不同,sort函数认为左边是优先值(排序时,左边排前面))
顺带一提,上面成员函数的运算符重载()函数的const是一个声明,表示这个函数不会修改形参,(加个保险,也可以不写)
2.3.2Lambda表达式方法
C++11新引入的方式,提供了Lambda表达式,可以用于给STL算法传递信息。
相关内容参考《C++ Primer Plus》18.4.2 为何使用lambda
- 和函数相比:很多程序员认为让定义位于使用的地方附近很有用,便于查看,修改代码的时候,涉及的内容放在一起便于阅读和编辑。比如函数内无法定义其他函数,但是可以定义Lambda表达式,这样就可以更加自由的把定义放在使用的地方旁边。
- 和仿函数相比:实现更加简洁,和函数有点像
捕获功能这里不讲,和优先队列没什么关系
3. 其他常见操作
#include<queue>
using namespace std;
priority_queue<int, vector<int>, greater<int>> minq;//声明队列
minq.push(i);//加入一个元素
minq.top();//堆顶元素
minq.pop();//推出堆顶元素
minq.empty();//是否为空
//没有清空函数,请使用以下方式清空
while(minq.empty())minq.pop();//方式一
minq = priority_queue<int, vector<int>, greater<int>>();//方式二,重新初始化
参考文献
C++标准库 自修教程与参考手册》10.3Priority Queues(优先队列) p453
C++标准库 自修教程与参考手册》8.1.1仿函数可当作排序准则(Sort Criteria) p294