浅谈C++ STL中的优先队列(priority_queue)
从我以前的博文能看出来,我是一个队列爱好者,很多并不是一定需要用队列实现的算法我也会采用队列实现,主要是由于队列和人的直觉思维的一致性导致的。
今天讲一讲优先队列(priority_queue),实际上,它的本质就是一个heap,我从STL中扒出了它的实现代码,大家可以参考一下。
首先函数在头文件<queue>中,归属于命名空间std,使用的时候需要注意。
队列有两种常用的声明方式:
std::priority_queue<T> pq;
std::priority_queue<T, std::vector<T>, cmp> pq;
第一种实现方式较为常用,接下来我给出STL中的对应声明,再加以解释。
template<class _Ty,
class _Container = vector<_Ty>,
class _Pr = less<typename _Container::value_type> >
class priority_queue
大家可以看到,默认模板有三个参数,第一个是优先队列处理的类,第二个参数比较有特点,是容纳优先队列的容器。实际上,优先队列是由这个容器+C语言中关于heap的相关操作实现的。这个容器默认是vector,也可以是dequeue,因为后者功能更强大,而性能相对于vector较差,考虑到包装在优先队列后,后者功能并不能很好发挥,所以一般选择vector来做这个容器。第三个参数比较重要,支持一个比较结构,默认是less,默认情况下,会选择第一个参数决定的类的<运算符来做这个比较函数。
接下来开始坑爹了,虽然用的是less结构,然而,队列的出队顺序却是greater的先出!就是说,这里这个参数其实很傲娇,表示的意思是如果!cmp,则先出列,不管这样实现的目的是啥,大家只能接受这个实现。实际上,这里的第三个参数可以更换成greater,像下面这样:
std::priority_queue<T, std::vector<T>, greater<T>> pq;
一般大家如果是自定义类就干脆重载<号时注意下方向了,没人在这里麻烦,这个选择基本上是在使用int类还想小值先出列时。
从上面的剖析我们也就知道了,想要让自定义类能够使用优先队列,我们要重载小于号。
class Student
{
int id;
char name[];
bool gender;
bool operator < (Student &a) const
{
return id > a.id;
}
};
就拿这个例子说,我们想让id小的先出列,怎么办,就要很违和的给这个小于符号重载成实际上是大于的定义。
如果我们不使用自定义类,又要用非默认方法去排序怎么办?就比如说在Dijkstra中,我们当然不会用点的序号去排列,无论是正序还是反序,我们想用点到起点的距离这个值来进行排序,我们怎样做呢?细心的读者在阅读我的有关Dijkstra那篇文章时应该就发现了做法——自定义比较结构。优先队列默认使用的是小于结构,而上文的做法是为我们的自定义类去定义新的小于结构来符合优先队列,我们当然也可以自定义比较结构。自定义方法以及使用如下,我直接用Dijkstra那篇的代码来说明:
int cost[MAX_V][MAX_V];
int d[MAX_V], V, s;
//自定义优先队列less比较函数
struct cmp
{
bool operator()(int &a, int &b) const
{
//因为优先出列判定为!cmp,所以反向定义实现最小值优先
return d[a] > d[b];
}
};
void Dijkstra()
{
std::priority_queue<int, std::vector<int>, cmp> pq;
pq.push(s);
d[s] = ;
while (!pq.empty())
{
int tmp = pq.top();pq.pop();
for (int i = ;i < V;++i)
{
if (d[i] > d[tmp] + cost[tmp][i])
{
d[i] = d[tmp] + cost[tmp][i];
pq.push(i);
}
}
}
}
优先队列的日常使用,了解上面那些就已经足够。下面给出优先队列的所有成员函数的STL实现方法,希望你看完没有一脸卧槽的感觉。c就是你声明时候的那个vector或者其他容器。
void push(value_type&& _Val)
{ // insert element at beginning
c.push_back(_STD move(_Val));
push_heap(c.begin(), c.end(), comp);
} template<class... _Valty>
void emplace(_Valty&&... _Val)
{ // insert element at beginning
c.emplace_back(_STD forward<_Valty>(_Val)...);
push_heap(c.begin(), c.end(), comp);
} bool empty() const
{ // test if queue is empty
return (c.empty());
} size_type size() const
{ // return length of queue
return (c.size());
} const_reference top() const
{ // return highest-priority element
return (c.front());
} void push(const value_type& _Val)
{ // insert value in priority order
c.push_back(_Val);
push_heap(c.begin(), c.end(), comp);
} void pop()
{ // erase highest-priority element
pop_heap(c.begin(), c.end(), comp);
c.pop_back();
}
浅谈C++ STL中的优先队列(priority_queue)的更多相关文章
- 浅谈C++ STL string容器
浅谈C++ STL string容器 本篇随笔简单讲解一下\(C++STL\)中\(string\)容器的使用方法及技巧. string容器的概念 其实\(string\)并不是\(STL\)的一种容 ...
- 浅谈C++ STL list 容器
浅谈C++ STL list 容器 本篇随笔简单讲解一下\(C++STL\)中\(list\)容器的使用方法和使用技巧. list容器的概念 学习过\(C++STL\)的很多同学都知道,\(STL\) ...
- 浅谈C++ STL vector 容器
浅谈C++ STL vector 容器 本篇随笔简单介绍一下\(C++STL\)中\(vector\)容器的使用方法和常见的使用技巧.\(vector\)容器是\(C++STL\)的一种比较基本的容器 ...
- 浅谈C++ STL queue 容器
浅谈C++ STL queue 容器 本篇随笔简单介绍一下\(C++STL\)中\(queue\)容器的使用方法和常见的使用技巧.\(queue\)容器是\(C++STL\)的一种比较基本的容器.我们 ...
- 浅谈C++ STL stack 容器
浅谈C++ STL stack 容器 本篇随笔简单介绍一下\(C++STL\)中\(stack\)容器的使用方法和常见的使用技巧. stack容器的概念 \(stack\)在英文中是栈的意思.栈是一种 ...
- 浅谈C++ STL deque 容器
浅谈C++ STL deque 容器 本篇随笔简单介绍一下\(C++STL\)中\(deque\)容器的使用方法及常见使用技巧. deque容器的概念 \(deque\)的意义是:双端队列.队列是我们 ...
- 转: 浅谈C/C++中的指针和数组(二)
转自:http://www.cnblogs.com/dolphin0520/archive/2011/11/09/2242419.html 浅谈C/C++中的指针和数组(二) 前面已经讨论了指针和数组 ...
- 转:浅谈C/C++中的指针和数组(一)
再次读的时候实践了一下代码,结果和原文不一致 error C2372: 'p' : redefinition; different types of indirection 不同类型的间接寻址 /// ...
- 转载 浅谈C/C++中的static和extern关键字
浅谈C/C++中的static和extern关键字 2011-04-21 16:57 海子 博客园 字号:T | T static是C++中常用的修饰符,它被用来控制变量的存贮方式和可见性.ext ...
随机推荐
- 【Unity 3D 游戏开发】Unity3D 入门 - 工作区域介绍 与 入门示例
一. 工作区域详解 1. Scence视图 (场景设计面板) scence视图简介 : 展示创建的游戏对象, 可以对所有的游戏对象进行 移动, 操作 和 放置; -- 示例 : 创建一个球体, 控制摄 ...
- Android后台消息推送-android学习之旅(71)
建议使用第三方的sdk,比如极光推送,小米推送,百度推送
- opencv读写视频,对感兴趣区域进行裁剪
作为小码农,本人最近想对一段视频的某个区域进行处理,因此要将该段视频区域裁剪出来,搜搜网上,发现没有痕迹,是故自己琢磨一下,左右借鉴,编了如下代码,目标得以实现,希望对你有用. #include &q ...
- 从驱动层分析android的Binder机制-android学习之旅(83)
前言及知识准备 Binder驱动程序 Service组件的注册和启动 Clinet应用获取服务 本次主要介绍Android平台下Binder进程间机制.从机制的组成出发,将按照Binder驱动程序.B ...
- 程序员的视角:java 线程
在我们开始谈线程之前,不得不提下进程. 无论进程还是线程都是很抽象的概念,有一个关于进程和线程很形象的比喻能帮我们更好的理解. 进程就像个房子,房子是一个包含了特定属性的容器,例如空间大小.卧室数量等 ...
- vector的简易实现
vector的简易实现整理自<数据结构与算法分析–C++描述(第3版)>3.4节“向量的实现”.详细可参考<STL源码分析>4.2节. 具体实现代码如下: #ifndef VE ...
- Mahout Bayes分类
Mahout Bayes分类器是按照<Tackling the Poor Assumptions of Naive Bayes Text Classiers>论文写出来了,具体查看论文 实 ...
- 开发composer包,打通github和packagist,并自动更新
1. 首先需要本地安装好composer,并配置好环境变量,在命令行输入composer,显示以下信息就表示正常安装 2. 在github对应项目的根目录下进行初始化composer 初始化完成后,就 ...
- ajax调用servlet
1.利用myecilpse建立一个web项目 2.导入需要的包: commons-beanutils.jar commons-collections-3.1.jar commons-lan ...
- OpenStack初识
一.它可以用来做什么? 想认识一个事物,必须先弄明白它是什么,能干什么.首先说一下,openstack是一个搭建云平台的一个解决方案,说他不是个软件,但是我觉得说是一个软件,能够让初学者更容易接受和理 ...