优先队列是计算机科学中的一类抽象数据类型。优先队列中的每个元素都有各自的优先级,优先级最高的元素最先得到服务;优先级相同的元素按照其在优先队列中的顺序得到服务,【不是普通队列的先进先出(FIFO)】。优先队列往往用**堆**来实现。🌴
🍽操作
优先队列至少需要支持下述操作:
- 插入带优先级的元素(insert_with_priority)
- 取出具有最高优先级的元素(pull_highest_priority_element)
- 查看最高优先级的元素(peek):O(1) 时间复杂度
其它可选的操作:
- 检查优先级高的一批元素
- 清空优先队列
- 批插入一批元素
- 合并多个优先队列
- 调整一个元素的优先级
🍿实现
初级实现
有许多简单低效的实现。如用一个有序的数组;或使用无序数组,在每次取出时搜索全集合,这种方法插入的效率为O(1),但取出时效率为O(n)。
典型实现
出于性能考虑,优先队列用堆来实现,具有O(log n)时间复杂度的插入元素性能,O(n)的初始化构造的时间复杂度。如果使用自平衡二叉查找树,插入与删除的时间复杂度为O(log n),构造二叉树的时间复杂度为O(n log n)。
从计算复杂度的角度,优先级队列等价于排序算法。
有一些特殊的堆为优先队列的实现提供了额外的性能:二叉堆的插入与提取操作的时间复杂度为O(log n),并可以常量时间复杂度的peek操作。二项堆提供了几种额外操作。斐波那契堆的插入、提取、修改元素优先级等操作具有分摊常量时间复杂度,[1],但删除操作的时间复杂度为O(log n)。Brodal queue(英语:Brodal queue)具有最糟糕情况下的常量复杂度但算法相当复杂因而不具有实用性。
对于整型、浮点型等具有有限值域的元素的数据类型,优先队列有更快的实现。
库实现
优先队列是计算机科学中的一类”容器数据类型”。
标准模板库(STL)以及1998年的C++标准确定优先队列是标准模板库的容器适配器模板。其实现了一个需要三个参数的最大优先队列:比较函数(缺省情况是小于函数less)、存储数据所用的容器类型(缺省情况是向量vector)以及指向序列开始和结束位置的两个迭代器。和标准模板库中其他的真实容器不同,优先队列不允许使用其元素类型的迭代器,而必须使用优先队列抽象数据类型的迭代器。标准模板库还实现了支持随机访问数据容器的优先队列–二叉最大堆。Boost C++库也在其中实现了堆结构。
🌸STL priority_queue 优先级队列 用法
优先级队列是一个容器适配器,它提供对最大(默认)元素的恒定时间查找,但以对数插入和提取为代价。可以提供用户提供的比较来更改顺序,例如 使用std :: greater 会使最小的元素显示为top()。使用priority_queue类似于在某些随机访问容器中管理堆,其好处是不会意外使堆无效。
🌻priority_queue 操作
对 priority_queue 进行操作有一些限制:
- push(const T& obj):将obj的副本放到容器的适当位置,这通常会包含一个排序操作。
- push(T&& obj):将obj放到容器的适当位置,这通常会包含一个排序操作。
- emplace(T constructor a rgs…):通过调用传入参数的构造函数,在序列的适当位置构造一个T对象。为了维持优先顺序,通常需要一个排序操作。
- top():返回优先级队列中第一个元素的引用。
- pop():移除第一个元素。
- size():返回队列中元素的个数。
- empty():如果队列为空的话,返回true。
- swap(priority_queue& other):和参数的元素进行交换,所包含对象的类型必须相同。
语法
1 2
| template <class Type, class Container= vector <Type>, class Compare= less <typename Container ::value_type>> class priority_queue
|
Type
要存储在 priority_queue 中的元素数据类型。
*Container 容器
用来实现 priority_queue 的基础容器的类型,比如vector,deque等,默认是vector .
Functional
一种提供函数对象的类型,该函数对象将两个元素值作为排序键进行比较,以确定其在 priority_queue 中的相对顺序。 此参数为可选自变量,默认值是二元谓词 less<typename Container::value_type>
。
1 2 3 4 5 6
| priority_queue <int,vector<int>,greater<int> > q;
priority_queue <int,vector<int>,less<int> >q;
|
基本类型优先队列的例子:
示例来自 c++优先队列(priority_queue)用法详解 - 华山青竹 - 博客园 (cnblogs.com) ,在此表示感谢。🍻🍻
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include<iostream> #include <queue> using namespace std; int main() { priority_queue<int> a;
priority_queue<int, vector<int>, greater<int> > c; priority_queue<string> b;
for (int i = 0; i < 5; i++) { a.push(i); c.push(i); } while (!a.empty()) { cout << a.top() << ' '; a.pop(); } cout << endl;
while (!c.empty()) { cout << c.top() << ' '; c.pop(); } cout << endl;
b.push("abc"); b.push("abcd"); b.push("cbd"); while (!b.empty()) { cout << b.top() << ' '; b.pop(); } cout << endl; return 0; }
|
运行结果:
1 2 3
| 4 3 2 1 0 0 1 2 3 4 cbd abcd abc
|
用pair做优先队列元素的例子:
规则:pair的比较,先比较第一个元素,第一个相等比较第二个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <iostream> #include <queue> #include <vector> using namespace std; int main() { priority_queue<pair<int, int> > a; pair<int, int> b(1, 2); pair<int, int> c(1, 3); pair<int, int> d(2, 5); a.push(d); a.push(c); a.push(b); while (!a.empty()) { cout << a.top().first << ' ' << a.top().second << '\n'; a.pop(); } }
|
运行结果:
用自定义类型做优先队列元素的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #include <iostream> #include <queue> using namespace std;
struct tmp1 { int x; tmp1(int a) {x = a;} bool operator<(const tmp1& a) const { return x < a.x; } };
struct tmp2 { bool operator() (tmp1 a, tmp1 b) { return a.x < b.x; } };
int main() { tmp1 a(1); tmp1 b(2); tmp1 c(3); priority_queue<tmp1> d; d.push(b); d.push(c); d.push(a); while (!d.empty()) { cout << d.top().x << '\n'; d.pop(); } cout << endl;
priority_queue<tmp1, vector<tmp1>, tmp2> f; f.push(b); f.push(c); f.push(a); while (!f.empty()) { cout << f.top().x << '\n'; f.pop(); } }
|
示例,来自 https://en.cppreference.com/w/cpp/container/priority_queue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #include<bits/stdc++.h> using namespace std;
#include <functional> #include <queue> #include <vector> #include <iostream>
template<typename T> void print_queue(T q) { while(!q.empty()) { std::cout << q.top() << ' '; q.pop(); } std::cout << '\n'; }
int main() {
std::priority_queue<int> q;
const auto data = {1,8,5,6,3,4,0,9,7,2};
for(int n : data) q.push(n);
print_queue(q);
std::priority_queue<int, std::vector<int>, std::greater<int>> q2(data.begin(), data.end()); print_queue(q2);
priority_queue<int,vector<int>,greater<int> >q22; for(int n:data) q22.push(n); print_queue(q22);
auto cmp = [](int left, int right) { return (left ^ 1) < (right ^ 1); }; std::priority_queue<int, std::vector<int>, decltype(cmp)> q3(cmp);
for(int n : data) q3.push(n);
print_queue(q3);
auto cmp2=[](int left, int right){return()};
}
|
输出
1 2 3 4
| 9 8 7 6 5 4 3 2 1 0 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 8 9 6 7 4 5 2 3 0 1
|
补充
剑指 Offer 40. 最小的k个数 - 力扣(LeetCode) (leetcode-cn.com) 这一题可使用优先队列的大顶推
🍉应用
优先队列常用于操作系统的任务调度,也是贪心算法的重要组成部分。
🌳参考文献
优先队列 - Wikiwand
std::priority_queue - cppreference.com
priority_queue 类 | Microsoft Docs
c++优先队列(priority_queue)用法详解 - 华山青竹 - 博客园 (cnblogs.com)