# 优先队列 优先队列(`priority_queue`)是 C++ 将二叉堆封装好的 STL 容器,能够快速地支持: - 插入元素 - 删除优先级最高(堆顶)的元素 - 访问优先级最高(堆顶)的元素 优先队列的使用和时间常数比 `set` 要小,因此应尽量使用优先队列替代 `set`。 ## 定义 ### 头文件 ```cpp #include <queue> ``` ### 定义 定义时需要三个参数:数据类型为 `TypeName`,底层容器为 `Container`,比较器函数对象为 `Compare`。当不指定 `Container` 和 `Compare` 时,默认底层容器为 `vector<TypeName>`,默认比较器为 `less<TypeName>`,此时优先级最高的元素为**最大**的元素。 ```cpp #include <queue> #include <vector> #include <functional> priority_queue<TypeName, Container, Compare> pq; priority_queue<int> pq1; // 默认为大根堆 // 小根堆,需要引入头文件 <functional> priority_queue<int, vector<int>, greater<int>> pq3; using pii = pair<int, int>; priority_queue<pii> pq4; priority_queue<pii, vector<pii>, greater<pii>> pq5; ``` ## 成员函数 $O(1)$ 时间复杂度: * `top()`:访问优先级最高的元素(优先队列不能为空,否则 `RE`) * `empty()`:判断容器是否为空 * `size()`:查询元素数量 * `a.swap(b)`:交换两个优先队列容器 `a` 和 `b` 的内容 ,要求 `a` 和 `b` 定义时的三个模板参数均相同;等同于 `swap(a, b)`。 $O(\log n)$ 时间复杂度: * `push(x)`:插入元素 * `pop()`:删除优先级最高的元素(优先队列不能为空,否则 `RE`) * `emplace(x)`:原地构造元素并插入,没有移动或拷贝 没有 `clear()` 方法。若要实现清空容器,可以在函数内局部定义容器然后退出函数时析构,或者不断 `pop()` 直至容器为空,或者与一个临时的空容器进行交换。 ```cpp priority_queue<int> pq1; void A1() { priority_queue<int> pq2; } void Clear() { for (; pq.size(); pq.pop()) { } priority_queue<int>().swap(pq); } ``` 关于原地构造 `emplace()`,请参考[[原地构造]]。 ## 自定义比较器 对于结构体类型,我们需要定义其比较规则才能用优先队列维护该类型元素。本处结构体定义如下: ```cpp struct Node { int x, id; }; ``` 阅读本处请参考[[自定义比较器]]。 请注意,比较器规则为**小元素靠前**,而优先队列中的优先级最高的元素为比较规则下**最大**的元素。 ### 方法 1:functor ```cpp // 最一般的写法 struct cmp { bool operator()(const Node &i, const Node &j) const { // 函数尾的 const 修饰符表示该函数不能对所在类的成员进行修改 return i.x < j.x; } }; priority_queue<Node, vector<Node>, cmp> pq; // 定义了一个 x 为关键字的大根堆 ``` ### 方法 2:重载 `<` 运算符 ```cpp struct Node { int x, id; bool operator<(const Node &i) const { return x > i.x; } }; priority_queue<Node> pq; // 定义了一个 x 为关键字的小根堆 ``` ### 方法 3:lambda 表达式 ```cpp auto cmp = [](const Node &i, const Node &j) { return i.x < j.x; }; priority_queue<Node, vector<Node>, decltype(cmp)> pq(cmp); // x 为关键字的大根堆 ``` ### 例子 1 ```cpp struct cmp{ bool operator()(int i, int j) { return i > j; } }; priority_queue<int> pq1; // 默认为大根堆 priority_queue<int, vector<int>, cmp> pq2; // 特别注意比较器的实现,跟 sort 比较器反着来 pq1.push(1); pq1.push(2); cout << pq1.top() << '\n'; // 2 pq1.pop(); cout << pq1.top() << '\n'; // 1 pq2.push(1); pq2.push(2); cout << pq2.top() << '\n'; // 1 pq2.pop(); cout << pq2.top() << '\n'; // 2 ``` ### 例子 2 ```cpp struct Student { int a, b, c; // 语数外 }; struct cmp { bool operator()(const Student &i, const Student &j) const { if (i.a != j.a) { return i.a < j.a; } if (i.b != j.b) { return i.b < j.b; } return i.c < j.c; } }; priority_queue<Student, vector<Student>, cmp> pq; // 按语数外从高到低排 int main() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); pq.push({100, 100, 90}); pq.push({100, 100, 100}); pq.push({100, 90, 100}); pq.push({90, 100, 100}); while (!pq.empty()) { Student x = pq.top(); pq.pop(); cout << x.a << ' ' << x.b << ' ' << x.c << '\n'; } return 0; } ```