priority_queue 又称为优先队列,其底层是用堆来进行实现的。
在优先队列中,队首元素一定是当前队列中优先级最高的那一个。
在任何时候往优先队列里面加入(push)元素,优先队列底层的数据结构堆(heap)会随时调整结构,使得每次的队首元素都是优先级最大的。
(这里的优先级是规定出来的)
单独定义一个 priority_queue:
priority_queue
name;
//这里的typename可以是任何基本类型,例如 int、double、char、结构体等,也可以是STL标准容器,例如vector、set、queue等。
和队列不一样的是,优先队列没有 front()函数与 back()函数,而只能通过 top()函数来访问队首元素(也可以称为堆顶元素),也就是优先级最高的元素。
#include
#include
using namespace std;
int main()
{
priority_queue q;
for (int i = 0; i < 6; i++)
{
q.push(i); //push(i) 用来将 i 压入队列,因此依次入队 0 1 2 3 4 5
}
printf("%d\n", q.top());
return 0;
}
(1)push()
push(x) 将令 x 入队,时间复杂度为 O(logN),其中 N 为当前优先队列中的元素个数。
(2)top()
top() 可以获得队首元素(即堆顶元素),时间复杂度为 O(1)。
(3)pop()
pop()令队首元素(即堆顶元素)出队,时间复杂度为 O(logN),其中 N 为当前优先队列中的元素个数。
#include
#include
using namespace std;
int main()
{
priority_queue q;
q.push(5);
q.push(2);
q.push(6);
printf("%d\n", q.top());
q.pop();
printf("%d\n", q.top());
return 0;
}
(4)empty()
empty() 检测 优先队列 是否为空,返回 true 则空,返回 false 则非空 。时间复杂度为 O(1)。
(5)size()
size() 返回 优先队列 中元素的个数,时间复杂度为 O(1)。
#include
#include
using namespace std;
int main()
{
priority_queue q;
if (q.empty() == true)
{
printf("EMPTY! \n");
}
else
{
printf("NOT EMPTY! \n");
}
q.push(5);
q.push(2);
q.push(6);
if (q.empty() == true)
{
printf("EMPTY! \n");
}
else
{
printf("NOT EMPTY! \n");
}
printf("%d\n", q.size());
return 0;
}
如何定义优先队列内的优先级是运用好优先队列的关键,下面分别介绍基本数据类型(例如 int,double,char)与结构体类型的优先级设置方法。
(1)基本数据类型的优先级设置
此处指的基本数据类型就是 int 型,double 型,char 型等可以直接使用的数据类型,优先队列对它们的优先级设置一般是数字大的优先级越高,因此队首元素就是优先队列内元素最大的那个(如果 char 型,则是字典序最大的)。
对于基本数据类型来说,下面两种优先队列的定义是等价的(以 int 为例,注意最后两个 > 之间有一个空格:
priority_queue q;
priority_queue,less >q;
可以发现,第二种定义方式的尖括号内多出了两个参数:一个是 vector
, 另一个是 less 。 其中 vector
(也就是第二个参数)填写的是来承载底层数据结构堆(heap)的容器,如果第一个参数是 double 型或 char 型,则此处只需要填写 vector 或 vector ; 而第三个参数 less
则是对第一个参数的比较类,less 表示数字大的优先级越大,而 greater 表示数字小的优先级越大。
#include
#include
using namespace std;
int main()
{
priority_queue,greater > q;
q.push(5);
q.push(2);
q.push(6);
printf("%d\n", q.top());
return 0;
}
(2)结构体的优先级设置
事实上,即便是基本数据类型,也可以使用下面讲解的结构体的优先级设置方法,只不过第三个参数的写法不太一样了。下面来看结构体的优先级设置方法。
现在希望按水果的价格高的为优先级高,就需要重载(overload)小于号 "<"。重载是指对已有的运算符进行重新定义,也就是说,可以改变小于号的功能。
可以看到,fruit 结构体中增加了一个函数,其中 “friend” 为友元。后面的 bool operator < (fruit f1,fruit f2) 对 fruit 类型的操作符 “<" 进行了重载(重载大于号会编译错误,因为从数学上来说只需要重载小于号,即 f1>f2 等价于判断 f2 把重载的函数写在结构体外面,同时用 struct 包装起来。 如下这样,即便是基本数据类型或者其他STL容器(例如set),也可以通过同样的方式来定义优先级。 注意:在这种情况下,需要用之前的第二种定义方式来定义优先队列:priority_queue 最后指出,如果结构体内的数据较为庞大(例如出现了字符串或者数组),建议使用引用来提高效率,此时比较类的参数中需要加上 "const" 和 "&"。如下: priority_queue 可以解决一些贪心问题,也可以对 Dijkstra 算法进行优化(因为优先队列的本质是堆)。 注意:使用 top()函数前,必须用 empty()判断优先队列是否为空,否则可能因为队空而出现错误。 #include
#include
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 的常见用途