一、简介
1、定义
优先队列相比普通队列,元素具有优先级,从原来的先进先出变成了最高级先出。换句话就是,原来队列是按照时间顺序从头到尾排列,现在变成了按照优先级高低从头到尾排列
2、基本使用方式
-
头文件:#include
-
定义:priority_queue<Type, Container, Functional>
注意:
①type:优先队列种存储的元素的数据类型(没有限制)
②container:必须是基于数组的容器,默认vector,deque也可以,但不能list
> 因为priority_queue 是基于堆(通常是二叉堆)实现的,而堆是一种完全二叉树,要求以 连续内存存储数据,需要容器支持随机访问,所以list不可以。为什么默认使用vector而不用deque(双端队列)呢?因为对于堆操作,vector比deque更加高效,deque的存储方式是分块连续(多个小连续块组合),虽然支持随机访问,但索引操作稍慢 -
两种形式:
①//升序队列,小顶堆
priority_queue <int,vector,greater > q;
②//降序队列,大顶堆
priority_queue <int,vector,less >q; -
默认类型
//对于基础类型 默认是大顶堆
//此时不用传入那三个参数,只用声明数据类型即可
priority_queue a; -
操作:
具有队列的所有特性,基本操作和队列相同
a.top 访问队头元素
b.empty 队列是否为空
c.size 返回队列内元素个数
d.push 插入元素到队尾 (并排序)
e.emplace 原地构造一个元素并插入队列
f.pop 弹出队头元素
g.swap 交换内容
二、自定义使用情况
1、用pair做优先队列元素
规则:先比较第一个元素,第一个相等的情况下比较第二个
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int main() {
// 定义优先队列,元素类型为 pair<int, int>
// 优先队列中存储 (优先级, 值),我们想要按优先级排序
priority_queue<pair<int, int>> pq;
// 插入一些元素到优先队列中
pq.push({3, 100}); // 插入元素 (3, 100)
pq.push({1, 200}); // 插入元素 (1, 200)
pq.push({4, 50}); // 插入元素 (4, 50)
pq.push({2, 300}); // 插入元素 (2, 300)
// 优先队列会自动将元素按第一个值 (优先级) 从大到小排序
cout << "Elements in priority queue (sorted by priority):" << endl;
// 遍历优先队列,输出元素
while (!pq.empty()) {
// 获取堆顶元素(具有最高优先级的元素)
pair<int, int> top = pq.top();
pq.pop(); // 移除堆顶元素
// 输出元素:优先级 和 值
cout << "Priority: " << top.first << ", Value: " << top.second << endl;
}
return 0;
}
结果:
Elements in priority queue (sorted by priority):
Priority: 4, Value: 50
Priority: 3, Value: 100
Priority: 2, Value: 300
Priority: 1, Value: 200
2、用自定义类型(排序规则)做优先队列元素部分
(1)什么是自定义类型
自定义类型通常是用户定义的结构体 (struct) 或类 (class),用于存储多个相关的数据字段
(2)实现方式1:重载<运算符
①概念
重载 < 运算符是在自定义类型中直接定义比较规则的最直接方法。std::priority_queue 默认使用最大堆(std::less),其排序规则依赖于元素的 < 运算符。因此,重载 < 运算符可以让优先队列理解自定义类型的比较逻辑。
②步骤
- 定义一个自定义类型(struct 或 class)。
- 在该类型中重载 < 运算符。
- 使用优先队列(std::priority_queue)存储该类型的对象,优先队列会自动按照定义的 < 运算符排序。
③重载函数示例
bool operator<(const Task& other) const {
return priority < other.priority; // 优先级大的排在堆顶
}
};
const Task& other:
Task&:参数 other 是要与当前对象 (this) 比较的另一个 Task 对象,使用引用传递以避免拷贝。
const:表示这个参数不会被修改,保证了安全性和一致性(const常和&联合起来用,既能够保证不被修改,也能避免再拷贝一份)
④代码示例
#include <iostream>
#include <queue>
#include <string>
#include <vector>
using namespace std;
// 定义任务结构体
struct Task {
int priority; // 优先级
string name; // 任务名
int duration; // 预计时间
// 重载 `<` 运算符
// 这里定义 "优先级高的任务优先"
bool operator<(const Task& other) const {
return priority < other.priority; // 优先级大的排在堆顶
}
};
int main() {
// 定义优先队列,元素类型为 Task
priority_queue<Task> pq;
// 插入任务到优先队列中
pq.push({3, "Task A", 10}); // 插入任务 (优先级: 3, 名称: Task A, 时长: 10)
pq.push({5, "Task B", 5}); // 插入任务 (优先级: 5, 名称: Task B, 时长: 5)
pq.push({1, "Task C", 15}); // 插入任务 (优先级: 1, 名称: Task C, 时长: 15)
// 输出优先队列内容
cout << "Tasks in priority queue:" << endl;
while (!pq.empty()) {
Task top = pq.top(); // 获取堆顶任务
pq.pop(); // 移除堆顶任务
cout << "Priority: " << top.priority
<< ", Name: " << top.name
<< ", Duration: " << top.duration << " mins" << endl;
}
return 0;
}
结果:
Tasks in priority queue:
Priority: 5, Name: Task B, Duration: 5 mins
Priority: 3, Name: Task A, Duration: 10 mins
Priority: 1, Name: Task C, Duration: 15 mins
这个原理就是你直接在数据类型里面定义比较函数,当你的数据类型确定了以后,你再使用这个优先队列,它就会按照数据类型里面的优先级
需要注意的是:
关于<的实际含义
bool operator<(const Person& other) const {
return age > other.age; // 升序排列:年龄小的优先
}
为什么这里代表的意思是年龄小的优先?
因为优先队列的默认情况是最大堆,也就是较小的元素排在较大的元素的后面,所以这里<的意思就是小的排在后面的意思。那么在重载中,为了让年龄小的元素优先出现在堆顶,我们就需要和原来的反着来,那也就是在return的过程中让大的排在前面。或者也可以直接记住一句口诀:升大降小
3、使用自定义比较器
如果不想修改结构体本身(例如不想重载<运算符),可以通过自定义比较器实现排序
①什么是自定义比较器
自定义比较器是一个函数或函数对象,用于定义两个元素之间的比较规则。
- 默认规则: STL 容器如 priority_queue 默认使用 大于运算符(<) 来比较元素。
- 对于最大堆,priority_queue 按降序排列(堆顶为最大值)。
- 对于 std::sort,按升序排列。
- 自定义规则: 通过提供一个自定义比较器,可以按照特定逻辑排序元素。例如:
- 按字段排序(如优先级)。
- 按自定义规则(如绝对值、特定条件等)。
②通过函数对象(仿函数)
仿函数是一个重载了 () 运算符的类或结构体。它是自定义比较器的最常用方式
③代码示例
#include <iostream>
#include <queue>
#include <string>
using namespace std;
// 定义任务结构体
struct Task {
int priority; // 优先级
string name; // 任务名称
Task(int p, string n) : priority(p), name(n) {}
};
// 自定义比较器(仿函数)
//operator() 是仿函数的核心,它重载了 () 运算符
struct CompareTask {
bool operator()(const Task& t1, const Task& t2) {
return t1.priority < t2.priority; // 优先级高的任务排在堆顶
}
};
int main() {
// 定义优先队列,指定自定义比较器
priority_queue<Task, vector<Task>, CompareTask> pq;
// 插入任务
pq.push(Task(3, "Task A")); // 优先级 3
pq.push(Task(5, "Task B")); // 优先级 5
pq.push(Task(1, "Task C")); // 优先级 1
// 输出优先队列中的内容
cout << "Tasks in priority queue:" << endl;
while (!pq.empty()) {
Task top = pq.top(); // 获取堆顶任务
pq.pop();
cout << "Priority: " << top.priority
<< ", Name: " << top.name << endl;
}
return 0;
}
结果:
Tasks in priority queue:
Priority: 5, Name: Task B
Priority: 3, Name: Task A
Priority: 1, Name: Task C
这种情况更适合:比如说你不想直接在数据类型里面定义比较类型,就可以用这种自定义比较器的形式,这种方式的核心就是在一个结构体中定义一种方法,然后将函数的实现改为这种结构体,注意是结构体!
参考文章
1、https://www.cnblogs.com/huashanqingzhu/p/11040390.html