一、概念
std::priority_queue
是 C++ 标准模板库(STL)中的一种适配器容器,用于实现优先队列数据结构。与普通的std::queue
不同,std::priority_queue
中的元素会根据优先级排序,优先级最高的元素总是位于队列的前端(即可以通过top()
函数访问到)。这是通过使用堆(通常是大堆)来实现的。
通过上面的陈述可以了解到:进入priority_queue的数据会按一定规则——优先级排序。
那这是按照什么规则排序的呢?
- priority_queue 内部使用的是一种特殊的二叉树结构完全二叉树的结构,排序:大堆或者小堆(默认的大堆)
- 大堆:每个节点的值都大于或等于其子节点的值(根节点是最大值)
- 小堆:每个节点的值都小于或等于其子节点的值(根节点是最小值)
因为二叉树是一个比较复杂且巧妙的内容,如果仔细介绍需要花费很大的篇幅,这里只是简单的一笔带过,想要了解更多的关于二叉树大堆小堆,建堆规则,向上调整向下调整等知识建议先系统学习数据结构:树部分。
priority_queue中的 top() 会是数值最大值或者最小值(大堆堆顶是最大值,小堆堆顶是最小值)
二、使用
了解priority_queue的底层结构之后,priority_queue的使用非常的简单!!
2.2 priority_queue类模板定义
priority_queue类的模板相较于queue是更复杂一点的,也是我们学习的难点:(源定义)
template<
class T,
class Container = std::vector<T>,
class Compare = std::less<typename Container::value_type>
>
class priority_queue{
... ...
}
上面的源码中类模板给了缺省值:默认容器是vector,默认生成的是大堆
注意:Compare = less<> 是大堆
我们一点一看:先去掉缺省值:
//template<class T,class Container,class Compare >
template<
class T,
class Container,
class Compare >
class priority_queue{
... ...
}
这个还是容易理解:
- T:数据类型
- Containter(容器):使用什么容器,(结合缺省值来看:默认容器是vector<T>)
- Compare(比较):建大堆还是小队
当这个理解之后我们来看看Compare的缺省值是什么?
template<
class T,
class Container = std::vector<T>,
class Compare = std::less<typename Container::value_type>
>
std::less
是一个标准库中的模板类,它实现了一个简单的比较函数,用于判断左操作数是否小于右操作数:
template <typename T>
struct less {
constexpr bool operator()(const T& left, const T& right) const {
return left < right;
}
};
但是它在less<>里面的Container::value_type又是什么意思呢?
class Compare = std::less<typename Container::value_type>
Container::value_type
:这个类型定义是所有标准容器的成员类型,用于指代前面容器存储的元素类型。例如,对于 std::vector<int>
,Container::value_type
就是 int
。
由于有些复杂这里通过图形帮大家更直观的理解:
下面再结合代码来看这部分内容:
假设这里定义了一个priority_queue对象 pq:
std::priority_queue<int> pq;
在这个例子中,std::priority_queue
实际上等价于:
std::priority_queue<int, std::vector<int>, std::less<int>> pq;
只不过前面用了缺省值,下面显示模板实例化。
大堆的简单使用:
#include <iostream>
#include <queue>
#include <vector>
int main() {
std::priority_queue<int> maxHeap;//使用缺省,默认vector默认less(大堆)
maxHeap.push(10);//插入一些无序的值
maxHeap.push(20);//数据,数据会根据大堆的规则自动排序
maxHeap.push(5);
maxHeap.push(70);
maxHeap.push(56);
//打印堆顶元素
std::cout << "堆顶 : " << maxHeap.top() << std::endl;
maxHeap.pop(); //删除一个元素之后打印
std::cout << "删除一个元素,堆顶: " << maxHeap.top() << std::endl;
std::cout << "maxHeap: " << std::endl;
//打印并删除
while (!maxHeap.empty()) {
std::cout << maxHeap.top() << " ";
maxHeap.pop();
}
return 0;
}
运行结果:
堆顶 : 70
删除一个元素,堆顶: 56
maxHeap: 56 20 10 5
当理解了大堆之后,以下是一个建小堆的示例:
注意:小堆必须显示模板实例化,通过显式传递 std::greater<T>
作为 Compare
参数。
#include <iostream>
#include <queue>
#include <vector>
int main() {
// 使用 std::greater<int> 创建最小堆
std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap;
minHeap.push(10);//插入一些无序的值
minHeap.push(20);
minHeap.push(5);
minHeap.push(70);
minHeap.push(56);
std::cout << "小堆 Top: " << minHeap.top() << std::endl; // 输出 5
minHeap.pop();
std::cout << "删除一个元素后,小堆 Top: " << minHeap.top() << std::endl; // 输出 10
return 0;
}
std::greater<T>
作为 Compare
参数和std::less<T>的用法一摸一样
。
Compare<greater<T>
>建小堆
Compare<less<T>
>建大堆
最后:
priority_queue不支持使用list容器!!
priority_queue不支持使用list容器!!
priority_queue不支持使用list容器!!
priority_queue
不支持的容器包括那些不提供随机访问迭代器或者不支持push_back
和pop_back
操作的容器,list
只支持双向迭代器,不支持随机访问,所以不能用作std::priority_queue
的底层容器。
2.1 priority_queue接口
empty()
:检查队列是否为空。size()
:返回队列中的元素数量。top()
:返回堆顶(根节点)的元素。push(const T& value)
:将元素插入优先队列中。pop()
:移除优先级最高的元素。- swap():交换两个优先队列的内容。
接口函数的使用非常的简单,构造函数在这里没有详细讨论,因为和queue的构造函数用法一样。其余接口函数与适配器stack和queue相似度很高,所以我们直接看使用。
示例
一个示例使用 deque
作为底层容器的 priority_queue
,并将它配置为小堆,结合了大部分常用接口函数的使用。(仅参考)
#include <iostream>
#include <queue>
#include <deque>
#include <functional> // 使用 std::greater需要的头文件
int main() {
// 使用 std::deque 作为底层容器,std::greater<int> 作为比较器,创建一个小堆
std::priority_queue<int, std::deque<int>, std::greater<int>> minHeap;
// 向堆中添加元素
minHeap.push(30);
minHeap.push(10);
minHeap.push(20);
minHeap.push(5);
minHeap.push(15);
minHeap.push(80);
minHeap.push(27);
minHeap.push(67);
// 打印堆顶元素 (最小元素)
std::cout << "堆顶: " << minHeap.top() << std::endl;
// 检查堆是否为空
if (!minHeap.empty()) {
std::cout << "堆不为空." << std::endl;
}
// 打印堆的大小
std::cout << "堆的大小: " << minHeap.size() << std::endl;
// 打印并移除所有元素
std::cout << "打印并移除所有元素:" << std::endl;
while (!minHeap.empty()) {
std::cout << minHeap.top() << " "; // 打印堆顶元素
minHeap.pop(); // 移除堆顶元素
}
std::cout << std::endl;
// 重新插入元素,展示 swap 函数
minHeap.push(100);
minHeap.push(50);
std::cout << "清除后重新插入:" << std::endl;
std::cout << "堆顶: " << minHeap.top() << std::endl;
// 创建另一个堆
std::priority_queue<int, std::deque<int>, std::greater<int>> anotherHeap;
anotherHeap.push(70);
anotherHeap.push(60);
// 交换两个堆
minHeap.swap(anotherHeap);
// 交换后的堆顶元素
std::cout << "交换后的堆顶元素: " << minHeap.top() << std::endl;
return 0;
}
运行结果:
堆顶: 5
堆不为空.
堆的大小: 8
打印并移除所有元素:
5 10 15 20 27 30 67 80
清除后重新插入:
堆顶: 50
交换后的堆顶元素: 60