C++STL容器priority_queue(优先队列)简述
- 优先队列的默认实现
- 使用优先队列来实现小根堆
- 优先队列+结构体
- 自定义优先队列的比较规则
优先队列的默认实现
STL容器中提供了 priority_queue(优先队列) 来实现类似堆的功能。
为了方便说明其用法,接下来的讲述中直接将 priority_queue 看做堆来讲述。
和用于排序的 sort 函数一样,priority_queue 默认的比较规则都是 <
(小于号)。
sort 默认会根据小于号将元素从小到大排序;
priority_queue 中的元素默认是根据小于号的比较规则将最大的作为其堆顶元素。这个跟 sort 的思路有点不一样, priority_queue 是 “我比你小,则我把你推到顶上去” 的意思。
就是说,默认情况下,priority_queue 中的元素总是最大的那个作为堆顶元素。
所以默认的 priority_queue 是一个大根堆。
定义一个 priority_queue 的一般格式为:
priority_queue<类型名> 容器名;
其最常用的成员方法有:
push(a)
:往堆中推入一个元素a
;top()
:获得堆顶元素;pop()
:弹出堆顶元素;empty()
:判断容器是否为空(为空返回true);size()
:返回目前该容器中包含元素的个数。
示例程序:
#include
using namespace std;
priority_queue que;
int main() {
for (int i = 3; i <= 6; i ++)
que.push(i);
que.push(1);
que.push(8);
cout << "size = " << que.size() << endl;
while (!que.empty()) {
cout << que.top() << ",";
que.pop();
}
return 0;
}
输出结果:
size = 6
8,6,5,4,3,1,
使用优先队列来实现小根堆
默认的比较规则是 <
(小于号),但是我们也可以在定义 priority_queue 的时候讲规则进行修改。比如下面的代码段就定义了一个大根堆:
priority_queue, greater > que;
其中,priority_queue 后的尖括号中:
int
表示数据类型;vector
表示数据的存储方式,在这里是使用 vector 存储(据说这样写上会快一些,不过我这里主要还是为了占一个位置,方便写第三个参数);greater
表示比较规则,这个greater
对应的比较规则就是>
(大于号),即 “我比你大,我把你推到顶上去”。
需要注意的是,如果 priority_queue 存储的是别的类型的数据,则对应的数据类型都得进行相应的修改,如下下面的代码段定义了一个存储 double 类型数据的小根堆:
priority_queue, greater > que;
示例程序(优先队列实现小根堆):
#include
using namespace std;
priority_queue, greater > que;
int main() {
for (int i = 3; i <= 6; i ++)
que.push(i);
que.push(1);
que.push(8);
cout << "size = " << que.size() << endl;
while (!que.empty()) {
cout << que.top() << ",";
que.pop();
}
return 0;
}
输出结果:
size = 6
1,3,4,5,6,8,
优先队列+结构体
对于结构体类型的变量来说,默认没有 <
(小于号),这种情况下直接使用该结构体类型的 priority_queue 显然是不行的(会报错)。
所以可以考虑为新定义的结构体类型定义一个 <
的功能,这种操作被称作 重载运算符。
比如,下面的程序中,我将定义一个名为 Node 的结构体并为其重载 <
运算符(因为 priority_queue 默认看的就是 <
运算符),并实现一个 Node 类型的优先队列。示例程序:
#include
using namespace std;
struct Node {
int x, y;
// 重载小于号运算符
bool operator < (const Node b) const {
return x < b.x || x == b.x && y < b.y;
}
};
priority_queue que; // 使用Node默认的比较规则
int main() {
que.push({3, 5});
que.push({2, 4});
que.push({1, 3});
que.push({4, 2});
que.push({3, 3});
while (!que.empty()) {
Node u = que.top();
que.pop();
cout << "(" << u.x << " , " << u.y << ")" << endl;
}
return 0;
}
程序输出结果:
(4 , 2)
(3 , 5)
(3 , 3)
(2 , 4)
(1 , 3)
自定义优先队列的比较规则
有的时候,有的数据类型可能已经封装好了 <
运算符,或者其 <
运算符还有别的用处,这种情况下我们不能再为其重载 <
运算符,那么这个时候怎么办呢?
回顾一下,在使用 sort 函数的时候,我们定义过比较函数(一般取名为 cmp
,国际惯例)。
然后在 sort 的时候讲 cmp 函数作为 sort 函数的第三个参数。
在 priority_queue 中也可以使用类型的功能,只不过 priority_queue 使用的是 比较结构体。
我们可以定义一个名为 Cmp 的结构体并重载其 ()
运算符,然后将其作为 priority_queue 定义时尖括号中的第三个参数。比如,下面的程序为 Node 类型匹配了一个对应的 Cmp 类型,并使用 Cmp 的比较规则实现了一个优先队列。
#include
using namespace std;
struct Node {
int x, y;
};
struct Cmp { // 比较结构体
bool operator () (Node &a, Node &b) {
return a.x < b.x || a.x == b.x && a.y < b.y;
}
};
priority_queue, Cmp> que; // 定义que时使用Cmp作为比较规则
int main() {
que.push({3, 5});
que.push({2, 4});
que.push({1, 3});
que.push({4, 2});
que.push({3, 3});
while (!que.empty()) {
Node u = que.top();
que.pop();
cout << "(" << u.x << " , " << u.y << ")" << endl;
}
return 0;
}
输出结果:
(4 , 2)
(3 , 5)
(3 , 3)
(2 , 4)
(1 , 3)