一、什么是优先队列?
优先队列就是一个会自动排序的队列。
普通队列 vs 优先队列
cpp
// 普通队列:先进先出
queue<int> q;
q.push(3);
q.push(1);
q.push(2);
// 取出顺序:3, 1, 2
// 优先队列:按大小排序
priority_queue<int> pq;
pq.push(3);
pq.push(1);
pq.push(2);
// 取出顺序:3, 2, 1(从大到小)
优先队列的基本用法
1. 头文件
cpp
#include <queue>
2. 声明方式
cpp
// 方式1:最大堆(默认)
priority_queue<int> pq1; // 最大的元素在顶部
// 方式2:最小堆
priority_queue<int, vector<int>, greater<int>> pq2; // 最小的元素在顶部
// 方式3:自定义比较
priority_queue<Node*, vector<Node*>, Compare> pq3; // 按自定义规则排序
三、常用操作
cpp
priority_queue<int> pq;
// 添加元素
pq.push(10);
pq.push(5);
pq.push(20);
// 访问顶部元素
int top = pq.top(); // 返回20(最大值)
// 删除顶部元素
pq.pop(); // 删除20
// 判断是否为空
if (pq.empty()) { }
// 获取大小
int size = pq.size(); // 2
四、三种声明方式详解
方式1:最大堆(默认)
cpp
priority_queue<int> pq;
// 等价于
priority_queue<int, vector<int>, less<int>> pq;
特点: 最大的元素在顶部
cpp
pq.push(3);
pq.push(1);
pq.push(2);
cout << pq.top(); // 输出 3
方式2:最小堆
cpp
priority_queue<int, vector<int>, greater<int>> pq;
特点: 最小的元素在顶部
cpp
pq.push(3);
pq.push(1);
pq.push(2);
cout << pq.top(); // 输出 1
方式3:自定义类型
cpp
struct Node {
int weight;
int ascii;
};
struct Compare {
bool operator()(Node a, Node b) {
// 返回true表示a的优先级低于b
if (a.weight != b.weight) {
return a.weight > b.weight; // 权值小的优先
}
return a.ascii > b.ascii; // ASCII小的优先
}
};
priority_queue<Node, vector<Node>, Compare> pq;
优先队列本质上就是一种"每次都能快速取出最大或最小元素"的数据结构,你可以把它理解成一个"自动排序的队列",但它不是完全排序的,而是始终保证队头元素是当前优先级最高的那个;在 C++ 里,默认是大根堆,也就是每次取出最大值。
先说最核心的特点;普通队列是先进先出,而优先队列是"优先级高的先出",至于谁优先级高,是由比较规则决定的;底层实现一般是堆(heap),所以插入和删除的时间复杂度都是 O(log n),查询堆顶是 O(1)。
C++ 中的基本用法很固定;定义一个优先队列 priority_queue pq,这样默认就是大根堆;如果你想要小根堆,就要写成 priority_queue<int, vector, greater> pq;这个写法的意思是用 vector 存数据,用 greater 作为比较函数,让小的优先出来。
常用操作就几个;push(x) 把元素加入队列;pop() 删除队头元素(注意不会返回值);top() 查看当前队头;empty() 判断是否为空;size() 查看元素个数;这些操作和普通队列类似,但语义不同。
再说一个非常常见的用法,就是存 pair;比如在 Dijkstra 里,我们通常存 pair<距离, 节点编号>,并且用小根堆,这样每次取出的都是当前距离最小的点;写法是 priority_queue<pair<long long,int>, vector<pair<long long,int>>, greater<pair<long long,int>>> pq;这里的比较是先比较 first,如果相等再比较 second。
优先队列最经典的应用有几个;第一是最短路径(Dijkstra),每次取当前距离最小的点;第二是哈夫曼树(合并果子),每次取最小的两个数;第三是一些"动态维护最大/最小值"的问题,比如不断加入元素并随时查询最大值。
最后讲一个容易踩的坑;priority_queue 不能像 vector 一样遍历,它不支持随机访问,如果你想看所有元素,只能不断 pop;另外,pop 只删除元素,不返回值,一定要先 top 再 pop;还有一点就是,如果你自定义结构体,一定要写比较规则,否则编译会报错。
总结一句:优先队列就是一个"随时能拿到当前最优元素"的工具,核心是堆结构,时间复杂度 O(log n),一旦题目出现"反复取最值",基本就可以考虑它。