【原创】优先队列 priority_queue 详解-程序员宅基地

技术标签: C++  原创  队列  # 心得  queue  优先队列  

优先队列

重要通知!!!!!!!!!!!!!
优先队列没有back()操作!!!!!
误人子弟Crloss已经自毙了!!!!!
同时更正了一些小问题。
——2018.11.03

引入

优先队列是一种特殊的队列,在学习堆排序的时候就有所了解,点“”查看。

那么优先队列是什么呢?
说白了,就是一种功能强大的队列。如果不太清楚队列,可以看看我这篇博客

它的功能强大在哪里呢?
四个字:自动排序

优先队列的头文件&&声明

首先,你需要

#include<queue>
using namespace std;//这个不是头文件但我也不晓得叫什么专业定语反正要和头文件一起开就是了!

这两个头文件东西。

using namespace std; 这句话,代表,使用一个叫做“std”的namespace,namespace里面封存了一系列东西,比方说奇异的数据结构和奇异的函数。当打开了namespace以后,就跟打开了头文件的本质是一样的,都是可以直接用它里面封存的函数。
不同之处在什么地方?就是不开namespace的使用,在你想用的(以std为例)函数面前加上“std::”即可。
例如,std::sort(a+1,a+1+N); 之类的。
感谢 @龙征天 的评论指点!

其次,一个优先队列声明的基本格式是:
priority_queue<结构类型> 队列名;
比如:

priority_queue <int> i;
priority_queue <double> d;

不过,我们最为常用的是这几种:

priority_queue <node> q;
//node是一个结构体
//结构体里重载了‘<’小于符号
priority_queue <int,vector<int>,greater<int> > q;
//不需要#include<vector>头文件
//注意后面两个“>”不要写在一起,“>>”是右移运算符
priority_queue <int,vector<int>,less<int> >q;

我们将在下文来讲讲这几种声明方式的不同。

优先队列的基本操作

与队列的基本操作如出一辙。
如果想要了解请点击这里,看关于队列的介绍。

以一个名为q的优先队列为例。

q.size();//返回q里元素个数
q.empty();//返回q是否为空,空则返回1,否则返回0
q.push(k);//在q的末尾插入k
q.pop();//删掉q的第一个元素
q.top();//返回q的第一个元素

优先队列的特性

上文已经说过了,自动排序
怎么个排法呢?
在这里介绍一下:

默认的优先队列(非结构体结构)

priority_queue <int> q;

这样的优先队列是怎样的?让我们写程序验证一下。

#include<cstdio>
#include<queue>
using namespace std;
priority_queue <int> q;
int main()
{
	q.push(10),q.push(8),q.push(12),q.push(14),q.push(6);
	while(!q.empty())
		printf("%d ",q.top()),q.pop();
}

程序大意就是在这个优先队列里依次插入10、8、12、14、6,再输出。
结果是什么呢?
14 12 10 8 6
也就是说,它是按从大到小排序的!

默认的优先队列(结构体,重载小于)

先看看这个结构体是什么。

struct node
{
	int x,y;
	bool operator < (const node & a) const
	{
		return x<a.x;
	}
};

这个node结构体有两个成员,x和y,它的小于规则是x小者小。
再来看看验证程序:

#include<cstdio>
#include<queue>
using namespace std;
struct node
{
	int x,y;
	bool operator < (const node & a) const
	{
		return x<a.x;
	}
}k;
priority_queue <node> q;
int main()
{
	k.x=10,k.y=100; q.push(k);
	k.x=12,k.y=60; q.push(k);
	k.x=14,k.y=40; q.push(k);
	k.x=6,k.y=80; q.push(k);
	k.x=8,k.y=20; q.push(k);
	while(!q.empty())
	{
		node m=q.top(); q.pop();
		printf("(%d,%d) ",m.x,m.y);
	}
}

程序大意就是插入(10,100),(12,60),(14,40),(6,20),(8,20)这五个node。
再来看看它的输出:
(14,40) (12,60) (10,100) (8,20) (6,80)

它也是按照重载后的小于规则,从大到小排序的。
↑好好康康这句话!(摘眼镜)

less和greater优先队列

还是以int为例,先来声明:

priority_queue <int,vector<int>,less<int> > p;
priority_queue <int,vector<int>,greater<int> > q;

再次强调:“>”不要两个拼在一起。

话不多说,上程序和结果:

#include<cstdio>
#include<queue>
using namespace std;
priority_queue <int,vector<int>,less<int> > p;
priority_queue <int,vector<int>,greater<int> > q;
int a[5]={10,12,14,6,8};
int main()
{
	for(int i=0;i<5;i++)
		p.push(a[i]),q.push(a[i]);
		
	printf("less<int>:");
	while(!p.empty())
		printf("%d ",p.top()),p.pop();	
		
	printf("\ngreater<int>:");
	while(!q.empty())
		printf("%d ",q.top()),q.pop();
}

结果:
less<int>:14 12 10 8 6 greater<int>:6 8 10 12 14

所以,我们可以知道,less是从大到小,greater是从小到大

作个总结

为了装13方便,在平时,建议大家写:

priority_queue<int,vector<int>,less<int> >q;
priority_queue<int,vector<int>,greater<int> >q;

平时如果用从大到小不用后面的vector<int>,less<int>,可能到时候要改成从小到大,你反而会搞忘怎么写greater<int>,反而得不偿失。

另一种排序方法

有可能遇到这种情况:心情不好不想用重载小于一个结构体的优先队列,要按照各种不一样的规则排序。

当然,如果不是优先队列而是数组,我们就会多写几个bool函数塞到sort里面来改变它的小于规则,比如:

struct node
{
	int fir,sec;
}arr[2030];

bool cmp1(node x,node y)
{
	return x.fir<y.fir;  //当一个node x的fir值小于另一个node y的fir值时,称x<y
}

bool cmp2(node x,node y)
{
	return x.sec<y.sec;  //当一个node x的sec值小于另一个node y的sec值时,称x<y
}

bool cmp3(node x,node y)
{
	return x.fir+x.sec<y.fir+y.sec;  //当一个node x的fri值和sec值的和小于另一个node y的fir值和sec值的和时,称x<y
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d %d",&arr[i].fir,&arr[i].sec);
	
	puts("\n--------------------");
	sort(arr+1,arr+1+n,cmp1); for(int i=1;i<=n;i++) printf("%d. {%d %d}\n",i,arr[i].fir,arr[i].sec);
}

	puts("\n--------------------");
	sort(arr+1,arr+1+n,cmp2); for(int i=1;i<=n;i++) printf("%d. {%d %d}\n",i,arr[i].fir,arr[i].sec);
}

	puts("\n--------------------");
	sort(arr+1,arr+1+n,cmp3); for(int i=1;i<=n;i++) printf("%d. {%d %d}\n",i,arr[i].fir,arr[i].sec);
}

因为不是整体所以就省略了验证环节(也就是说上面那个代码的正确性不保证)

但是优先队列可没有sort那么灵活想用什么作小于规则用什么作小于规则,它只会用一个固定的小于规则。
所以如果想把一个队列按不同的方式优先,就要:

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

int n;
struct node
{
	int fir,sec;
	void Read() {scanf("%d %d",&fir,&sec);}
}input;

struct cmp1
{
	bool operator () (const node &x,const node &y) const
	{
		return x.fir<y.fir;
	}
};//当一个node x的fir值小于另一个node y的fir值时,称x<y

struct cmp2
{
	bool operator () (const node &x,const node &y) const
	{
		return x.sec<y.sec;  
	}
};//当一个node x的sec值小于另一个node y的sec值时,称x<y

struct cmp3
{
	bool operator () (const node &x,const node &y) const
	{
		return x.fir+x.sec<y.fir+y.sec; 
	}
};//当一个node x的fri值和sec值的和小于另一个node y的fir值和sec值的和时,称x<y

priority_queue<node,vector<node>,cmp1> q1;
priority_queue<node,vector<node>,cmp2> q2;
priority_queue<node,vector<node>,cmp3> q3;

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) input.Read(),q1.push(input),q2.push(input),q3.push(input);
	
	printf("\ncmp1:\n");
	while(!q1.empty()) printf("(%d,%d) ",q1.top().fir,q1.top().sec),q1.pop();	
		
	printf("\n\ncmp2:\n");
	while(!q2.empty()) printf("(%d,%d) ",q2.top().fir,q2.top().sec),q2.pop();	
		
	printf("\n\ncmp3:\n");
	while(!q3.empty()) printf("(%d,%d) ",q3.top().fir,q3.top().sec),q3.pop();	
}

读入:

7
1 2
2 1
6 9
9 6
-100 100
-500 20
4000 -3000

输出:

cmp1:
(4000,-3000) (9,6) (6,9) (2,1) (1,2) (-100,100) (-500,20)

cmp2:
(-100,100) (-500,20) (6,9) (9,6) (1,2) (2,1) (4000,-3000)

cmp3:
(4000,-3000) (6,9) (9,6) (1,2) (2,1) (-100,100) (-500,20)

我们可以发现啊,priority_queue <int,vector<int>,less<int> > p;的那个less<int>其实就代表这个优先队列的小于规则,所以把这个换成cmp1就会有上述效果,desu~
所以说,所以说啦,一定要记得写全称!
搞定!

总结

优先队列到此就作了个小结。
其实不管是队列,还是优先队列,都不仅仅只有我讲的这些,还有更多可以探索。

应该入门级别都讲得差不多了,之后呢,在下就期待诸君的表演了。
学,无止境。


UPD (2019.10.31)

可持久化阅读博客,点此查看历史版本
现在你百度“优先队列”搜到的第一条,就是我UPD之前的历史版本了。
其实是此人不动声色地把我标题里的“【原创】”拿掉了,挂了个转载就转走了。

话说在很久以前的很长一段时间,我啊一直是百度“优先队列”的第一条。不晓得是因为我改了名还是我修正了没有back操作的错误还是我的自定义栏目被csdn拿掉了,就刷下去了。
当然看到我的历史版本很快顶替了这个位置我还是很高兴呢,我也把人们被那篇博客的称赞看作是对我自己的称赞,更喜的是一位哥们在那篇博客里评论了一句之后又点进超链接到我的博客下面又评论了一句一模一样的的。真的xswl
我之所以要写这几句话只是因为我最近太高兴了,几乎感觉不到压力,压力很小呢,就爱把能量发泄出去。真的没有任何批评指责的意思,你看我这句话都没加粗。
做人理应宽容大度,别人打你左脸还要伸右脸,亲亲,这边建议您把我的那些个超链接链接到的东西都转载过去哦。

反正是小事。
一个小人物因为自己的小成就被另一个小人物取代了而在这里无意义的悻悻,传播能量。
x s w l xswl xswl w x s l wxsl wxsl

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/c20182030/article/details/70757660

智能推荐

再见XShell!这款国产终端更好用!-程序员宅基地

文章浏览阅读826次。大家好,我是TJ一个励志推荐10000款开源项目与工具的程序员昨天和小伙伴闲聊的时候说起了SSH工具,TJ君心目中一款优秀的SSH工具需要具备以下几个特点:连接稳定;支持文件传输;易操作简..._shell工具国产

Git版本控制使用方法入门教程-程序员宅基地

文章浏览阅读299次。1. 概述对于软件版本管理工具,酷讯决定摒弃CVS而转向Git了。为什么要选择Git? 你真正学会使用Git时, 你就会觉得这个问题的回答是非常自然的。然而当真正需要用文字来回答时,却觉得文字好像不是那么够用。 咳,该则么回答呢?其实,关键的问题不在于如何回答这个问题。 问题的关键是公司已经决定使用它了。那么,我们的程序员们! 请开动你们的浏览器,请拿出你的搜索引擎工具,去_版本控制使用方法

67.二进制求和(简单)-程序员宅基地

文章浏览阅读25次。给你两个二进制字符串a和b,以二进制字符串的形式返回它们的和。

Python 文本处理和语义分析2 使用m3e对文本向量化_m3e-base 数据分析-程序员宅基地

文章浏览阅读1.3k次,点赞26次,收藏15次。向量化将会是下一阶段演进的目标。在过去的实践中,向量或者矩阵其实是最贴近工具端的。以sklearn为例,虽然原始数据可能还是自然语言,但是在最终执行 fit或者predict之前,数据一般都转为了矩阵形态(numpy)。也就是说,在pandas(原始数据)和最终结果(predict result)之间,是(短暂且必然)存在过矩阵的。后来,应该是有过类似以图搜图类的应用,向量化且持久化在数据库中开始兴起。_m3e-base 数据分析

cmd常用操作_source d:/jtsys.sql-程序员宅基地

文章浏览阅读186次。cmd常用操作,易忘…1,查看JDK版本:java -version2,执行sql文件:source 文件路径如:source d:/CGBWORK/jtsys.sql3,备份数据库在cmd窗口中(未登录的状态), 通过mysqldump命令备份数据库:格式: mysqldump -u用户名 -p 备份的库的名字 > 备份文件位置举例: 备份db40库中的所有数据(dept表及..._source d:/jtsys.sql

堆的结构实现与应用-程序员宅基地

文章浏览阅读1k次,点赞25次,收藏32次。我们只要记住关键的两点:1.堆必须是完全二叉树。2.堆要么是大堆,要么是小堆。

随便推点

jquery-隐藏和显示_jquery隐藏ul-程序员宅基地

文章浏览阅读1.4k次。jquery hide() / show()先上个效果图通过jquery的hide()show()方法来隐藏/显示html元素语法:$(selector).hide(speed,callback);$(selector).show(speed,callback);(可选的) speed 参数规定隐藏/显示的速度,可取以下值:"slow"、"fast" 或毫秒(可_jquery隐藏ul

JNI实战-Android深度学习模型部署_将深度学习模型封装成jni-程序员宅基地

文章浏览阅读511次。传统方式 java->javac -> .class->javah -jni->.h C/C++实现.h中声明的方法 添加并编写.mk文件 通过CMake工具 CMakeLists.txt 学习资源https://www.jianshu.com/p/b4431ac22ec2_将深度学习模型封装成jni

获取接口重定向后的url,两种方式:-程序员宅基地

文章浏览阅读895次。获取接口重定向后的url的方式

基于java Springboot+Vue+shiro前后端分离疫情防疫管理系统设计和实现2.0 免费源码+论文答辩资料获取_前后端分离java项目答辩论文-程序员宅基地

文章浏览阅读445次。基于java Springboot+Vue+shiro前后端分离疫情防疫管理系统设计和实现2.0目录研究背景主要特性功能:视频效果演示 :主要功能截图:系统首页:疫情数据分布图模拟:用户管理:角色控制:菜单权限:每日健康打卡:历史出行数据:外出报备申请:外出请假审核:疫情通知公告:疫情资料管理:注销修改密码:主要代码实现:主要数据表设计:表clock表file表go_out表info表sys_captcha (系统验证码)表sys_config (系统配置信息表)表sys_log (系统日志)表sys_m_前后端分离java项目答辩论文

graylog--splunk的开源替代-程序员宅基地

文章浏览阅读1.1k次。2019独角兽企业重金招聘Python工程师标准>>> ..._splunk 开源替代

从零开始的目标检测和关键点检测(一):用labelme标注数据集_数据标注labelme-程序员宅基地

文章浏览阅读2.3k次。从零开始的目标检测和关键点检测(一):用labelme标注数据集_数据标注labelme