摘自胡凡的《算法笔记》,仅作记录用!
前言:要使用优先对垒,应先添加头文件#include <queue>
,并在头文件下面加上using namespace std;
一、priority_queue的定义
其定义的写法和其他STL容器相同,typename可以是任意基本数据类型或容器priority_queue<typename> name;
二、priority_queue容器内元素的访问
和队列不一样的是,优先队列没有front()函数与back()函数,而只能通过top()函数来访问队首元素(也可以称为堆顶元素),也就是优先级最高的元素。
三、priority_queue常用函数
- push()
push(x)将x进行入队,时间复杂度为 O ( l o g N ) O(logN) O(logN),N为当前优先队列中的元素个数。 - top()
top()可以获得队首元素(即堆顶元素),时间复杂度为 O ( 1 ) O(1) O(1) - pop()
pop()令队首元素出队,时间复杂度为 O ( l o g N ) O(logN) O(logN),N为当前优先队列中的元素个数。 - empty()
检测优先队列是否为空,返回true则空,返回false则非空,时间复杂度为 O ( 1 ) O(1) O(1) - size()
返回优先队列内元素的个数,时间复杂度为 O ( 1 ) O(1) O(1)
四、priority_queue内元素优先级的设置
- 基本数据类型的优先级设置
- 此处指的基本数据类型就是int型、double型、char型等可以直接使用的数据类型,优先队列对它们的优先级设置一般是数字大的优先级越高,因此队首元素就是优先队列内元素最大的那个(如果是char型,则是字典序最大的)。对基本数据类型来说,下面两种优先队列的而定义是等价的(以int型为例,注意最后两个>之间有一个空格):
priority_queue<int> q;
/priority_queue<int, vector<int>, less<int> > q;
- 可以发现,第二种定义方式的尖括号内多出了两个参数:一个是
vector<int>
,另一个是less<int>
,其中vector<int>
填写的是来承载底层数据结构堆的容器,如果第一个参数是double型或char型,则此处只需要填写vector<double>
或vector<char>
;less<int>
表示数字大的优先级越大,而greater<int>
表示数字小的优先级越大
- 此处指的基本数据类型就是int型、double型、char型等可以直接使用的数据类型,优先队列对它们的优先级设置一般是数字大的优先级越高,因此队首元素就是优先队列内元素最大的那个(如果是char型,则是字典序最大的)。对基本数据类型来说,下面两种优先队列的而定义是等价的(以int型为例,注意最后两个>之间有一个空格):
- 结构体的优先级设置
- 现在有一个结构体,其中有水果的名称和价格。
struct fruit{
string name;
int price;
};
- 如果希望按水果的价格高的为优先级高,就需要重载小于号“<”。重载指的是对已有的运算符进行重新定义。也就是说,可以改变小于号的而功能。
struct fruit{
string name;
int price;
friend bool operator < (fruit f1, fruit f2){
return f1.price < f2.price;
}
};
- 可以看到,fruit结构体中增加了一个函数,其中
friend
为友元。后面的bool operator < (fruit f1, fruit f2)
对fruit类型的操作符<进行了重载(重载大于号会编译错误,因为从数学上来说只需要重载小于号,即f1>f2等价于判断f2<f1,而f1 == f2
则等价于判断!(f1<f2)&&!(f2<f1)
),函数内部为return f1.price<f2.price,因此重载后小于号还是小于号的作用。此时就可以直接定义fruit类型的优先队列,其内部就是以价格高的水果为优先级高。 - 同理,如果想要以价格低的水果为优先级高,那么只需要把return中的小于号改为大于号即可。
- 此处对小于号的重载与排序函数sort中的cmp函数有些相似,它们的参数都是两个变量,函数内部都是return了true或者false。事实上,这两者作用确实是类似的,只不过效果看上去似乎是“相反”的。在排序中,如果是return
f1.price>f2.price
,那么则是按价格从高到低排序,但是在优先队列中却是把价格低的放到队首。原因在于,优先队列本身默认的规则就是优先级高的放在队首,因此把小于号重载为大于号的功能时只是把这个规则反向了一下。 - 也可以把friend去掉,把小于号改成一对小括号,然后把重载的函数写在结构体外面,同时将其用struct包装起来。
struct cmp{
bool operator() (fruit f1,fruit f2){
return f1.price>f2.price;
}
};
-
在这种情况下,需要用之前讲解的第二种定义方式来定义优先队列
priority_queue<fruit, vector<fruit>, cmp > q;
-
最后指出,如果结构体内的数据较为庞大(例如出现了字符串或者数组),建议使用引用来提高效率,此时比较类的参数中需要加上
const和&
friend bool operator< (const fruit &f1,const fruit &f2){
return f1.price>f2.price;
}
bool operator() (const fruit &f1,const fruit &f2){
return f1.price>f2.price;
}
五、priority_queue的常见用途
- priority_queue可以解决一些贪心问题,也可以对Dijkstra算法进行优化(因为优先队列的本质是堆)
- 有一点需要注意的是,使用top函数前,必须用empty()判断优先队列是否为空,否则可能因为队空而出现错误。