← 目录 / 第十一章 · STL 标准模板库 / 11.7 priority_queue

11.7 priority_queue 优先队列

不是"先来后到",而是"谁大谁先出"——底层用二叉堆实现,永远能 O(1) 拿到当前最大(或最小)的元素。

本页目录

priority_queue(优先队列)总是把最大的元素放在队首。它不是简单的"先来后到",而是"谁大谁先出"。底层用二叉堆(binary heap)实现,插入和取出最大值都是 O(log n),比每次排序快得多。使用前需引入 <queue> 头文件(和 queue 同一个)。

大顶堆的两种视角:逻辑上是一棵树,物理上存成一个数组
9 堆顶 = 最大值 7 6 3 5 4 物理存储(数组): 9 [0] 7 [1] 6 [2] 3 [3] 5 [4] 4 [5]
大顶堆的核心规则:每个父节点的值,永远 ≥ 它的子节点。这保证了堆顶(数组下标 0)永远是整棵树里最大的元素,所以 top() 是 O(1)。插入或删除元素后,二叉堆会自动"上浮"或"下沉"调整,使其重新满足这个规则,整个过程是 O(log n)——这正是 priority_queue 的底层原理,使用时不需要手写,STL 已经帮你实现好了。
11.7.1 定义与初始化

默认情况下,priority_queue<int> 创建的就是一个大顶堆——也就是说,不管元素以什么顺序 push 进去,每次调用 top() 拿到的都是当前最大的那个元素。

C++ · priority_queue 的定义方式
1#include <iostream>
2#include <queue> // 必须包含这个头文件
3using namespace std;
4
5int main()
6{
7 priority_queue<int> pq; // 默认是大顶堆
8 pq.push(3);
9 pq.push(1);
10 pq.push(4);
11 pq.push(2);
12 return 0;
13}
⚠️
priority_queuestack / queue 一样是容器适配器,没有迭代器,不能遍历。想看里面所有元素,只能一边 top() 一边 pop(),直到 empty()
11.7.2 常用操作
C++ · 大顶堆:push / top / pop
1priority_queue<int> pq;
2pq.push(3);
3pq.push(1);
4pq.push(4);
5pq.push(2);
6
7cout << pq.top() << endl; // 输出:4——当前最大的元素
8pq.pop(); // 弹出 4
9cout << pq.top() << endl; // 输出:3——新的最大元素
10
11// 逐个弹出(从大到小)
12while (!pq.empty())
13{
14 cout << pq.top() << " "; // 输出:4 3 2 1
15 pq.pop();
16}
11.7.3 小顶堆(最小的在队首)

默认的 priority_queue 是大顶堆。如果想要"最小的元素优先",有两种常用写法:

C++ · 两种小顶堆写法
1// 写法一:用 greater(需要 vector 和 greater)
2priority_queue<int, vector<int>, greater<int>> min_pq;
3min_pq.push(3);
4min_pq.push(1);
5min_pq.push(4);
6cout << min_pq.top() << endl; // 输出:1——当前最小的元素
7
8// 写法二:竞赛常用技巧——存负数(简单省事)
9priority_queue<int> pq;
10pq.push(-3); // 存 -3
11pq.push(-1); // 存 -1
12pq.push(-4); // 存 -4
13cout << -pq.top() << endl; // 取出来再取反:1(最小的)
写法建议
greater<int>语义清晰,推荐在正式代码中使用
存负数打字更少,竞赛中很常见,但要小心负负得正的边界情况
📎
greater<int> 需要的头文件:greater<int> 这个比较规则定义在 <functional> 头文件里。大多数编译器的 <queue> 内部已经间接包含了它,所以上面的代码通常不加也能编译通过;但依赖这种"间接包含"并不是一个稳妥的习惯,显式加上 #include <functional> 更安全,也能让代码的依赖关系一目了然。
11.7.4 完整使用示例
C++ · priority_queue 综合示例
1#include <iostream>
2#include <queue>
3using namespace std;
4
5int main()
6{
7 priority_queue<int> pq;
8 pq.push(3);
9 pq.push(1);
10 pq.push(4);
11 pq.push(2);
12
13 cout << "堆顶: " << pq.top() << endl; // 输出:4
14 cout << "大小: " << pq.size() << endl; // 输出:4
15
16 // 从大到小依次弹出
17 cout << "依次弹出: ";
18 while (!pq.empty())
19 {
20 cout << pq.top() << " "; // 输出:4 3 2 1
21 pq.pop();
22 }
23 cout << endl;
24
25 return 0;
26}
📋 priority_queue 常用操作速查表

把本节出现过的方法按用途归类汇总,写代码时可以直接当参考卡用。

方法 / 写法分类作用
pq.push(x)修改插入元素,自动调整堆结构,O(log n)
pq.pop()修改删除堆顶元素,不返回值,O(log n)
pq.top()访问读取(不删除)堆顶元素,O(1)
pq.size() / pq.empty()容量元素个数 / 判断是否为空
priority_queue<int>定义默认大顶堆,最大值在堆顶
priority_queue<int, vector<int>, greater<int>>定义小顶堆,最小值在堆顶
🎯
什么时候用 priority_queue?Dijkstra 最短路算法、堆排序、Top-K 问题(找最大/最小的 K 个数)、任务调度(按优先级处理)……任何"每次都要拿当前最大/最小值"的场景,priority_queue 都比每次重新排序快得多。特别适合处理动态数据流的场景:数据不是一次性给好、排一次序就完事,而是不断有新数据陆续加入,你需要随时能拿到"当前"的最大值或最小值——这正是 priority_queue 能够一直保持高效(插入和取极值都是 O(log n))而重新排序做不到的地方。