C++ STL 优先队列priority_queue简述

本文介绍了优先队列的基本原理和使用方法,包括如何定义优先队列、插入和删除元素、读取队首元素等操作。

本文主要简单介绍优先队列的原理和用法,便于快速学习和查阅。

原理

优先队列和队列一样,只能从队尾插入元素,从队首删除元素。
优先队列中最大元素始终位于队首,所以,出队时并非按照先进先出的原则,而是将当前队列中最大的元素出队。
可以重载"<"操作,重新定义比较规则。

实现
  • 头文件
    优先队列头文件与队列头文件一样,使用优先队列时别忘了加上queue 的头文件。
#include<queue>
  • 定义
priority_queue<Data_Type> name;

Data_Type 为优先队列要存储的数据的类型,name 为该队列的名字。
例如:

priority_queue<int> pq;  // 命名为pq的有限队列存储整型数据
  • 插入元素
    运用push() 成员函数将元素入队。
    例如:
pq.push(1);
pq.push(2);
  • 读取元素数量
    size() 成员函数返回当前优先队列中元素的数量。
pq.size();
  • 判断队列是否为空
    运用empty() 成员函数判断队列是否为空。若为空,返回逻辑真,否则返回逻辑假。
pq.empty();
  • 读取队首元素
    运用成员函数top() 返回队首元素。
pq.top();

注意:只能返回元素的值,而不能删除队首元素。

  • 删除元素
    运用成员函数pop() 将队首元素出队。
pq.pop();

注意:只能删除队首元素,而不能返回队首元素的值。

以上操作其实和队列操作类似,至于"<"的重载,本文暂时不做讲解。

1. std::vector —— 动态数组 ✅ 特点: 底层为连续内的动态数组; 支持随机访问(O(1) 下标访问); 可以动态扩容(自动双倍增长); 是最常用、最灵活的容器。 ⏱ 时间复杂度: 操作 复杂度 访问元素 vec[i] O(1) 尾部插入/删除 push_back/pop_back 均摊 O(1) 中间插入/删除 O(n) 查找元素 O(n) 📌 使用场景: 替代普通数组,避免手动管理大小; 图(邻接表)、储输入数据、DP 数组等; 实现栈或队列(配合 push_back 和 pop_back); 💡 示例代码: 🔧 技巧: 使用 vec.reserve(n) 预分配空间,避免频繁扩容; vector<bool> 是特化版本,行为异常,慎用。 2. std::queue 和 std::priority_queue —— 队列与优先队列 ✅ std::queue 先进先出(FIFO)结构。 常用于 BFS、模拟任务调度等。 主要操作: 示例(BFS): ✅ std::priority_queue 默认是最大堆,顶部始终是最大元素; 常用于 Dijkstra、Huffman 编码、贪心算法等。 默认行为(大根堆): C++ std::priority_queue<int> pq; pq.push(3); pq.push(1); pq.push(4); std::cout << pq.top(); // 输出 4 小根堆写法: C++ std::priority_queue<int, std::vector<int>, std::greater<int>> min_pq; 自定义比较(结构体): ⚠️ 注意:priority_queue 不支持修改已有元素,也不能遍历。 3. std::stack —— 栈 后进先出(LIFO),适合 DFS、括号匹配、表达式求值等。 常用操作: C++ s.push(x); s.pop(); // 无返回值 s.top(); // 返回栈顶 示例: 4. std::deque —— 双端队列 双向队列,可在头尾高效插入/删除; 支持随机访问; push_front, pop_front, push_back, pop_back 均为 O(1); vector 扩容时会失效迭代器,而 deque 更稳定。 使用场景: 单调队列(滑动窗口最大值); BFS 中需要从前或后插入的情况; 实现双端队列类问题。 示例: 5. std::set / std::multiset —— 有序集合 ✅ std::set 内部基于红黑树实现,自动排序且去重; 插入、删除、查找均为 O(log n); 不支持下标访问,但可用迭代器遍历。 常用操作: 使用场景: 动态维护一个有序唯一集合; 查询前驱/后继(lower_bound / upper_bound); 替代平衡树。 ✅ std::multiset 允许重复元素的 set; 同样有序,支持 insert, erase, find, lower_bound 等; 删除单个元素时使用 erase(s.find(x)) 防止全部删除; erase(x) 会删掉所有等于 x 的元素! 6. std::map / std::unordered_map —— 关联容器(键值对) ✅ std::map 键值对容器,按键有序排列(红黑树); 插入、查找、删除:O(log n); 键自动排序,可遍历得到有序结果。 示例: ✅ std::unordered_map 基于哈希表实现; 平均操作复杂度 O(1),最坏 O(n); 不保证顺序; 在竞赛中常用于快速映射(如字符串 → ID、计数等)。 示例: C++ std::unordered_map<std::string, int> cnt; cnt["apple"]++; cnt["banana"]++; ⚠️ 注意事项: 哈希碰撞可能导致被卡(尤其在线评测系统),可加随机扰动防 hack; 对自定义类型需提供 hash 函数,否则不支持。 7. std::array —— 固定大小数组(C++11 起) 类似内置数组,但更安全、支持 STL 操作; 大小在编译期确定; 性能极高,适用于固定长度的小数组。 示例: C++ std::array<int, 5> arr = {1, 2, 3, 4, 5}; for (int x : arr) std::cout << x << " "; 8. std::pair 和 std::tuple —— 组合类型 ✅ std::pair<T1, T2> 储两个元素的组合; 常用于 map 键值、priority_queue 中带权值的节点; 支持字典序比较(<),便于排序。 示例: C++ std::pair<int, std::string> p = {3, "hello"}; std::vector<std::pair<int, int>> edges; edges.push_back({u, v}); ✅ std::tuple 多元组,可多个不同类型元素; 使用 std::make_tuple, std::tie, std::get 操作。 C++ auto t = std::make_tuple(1, "abc", 3.14); int a; std::string b; double c; std::tie(a, b, c) = t; 9. 其他实用技巧与组合使用 场景 推荐容器组合 图的邻接表表示 vector<vector<int>> 或 vector<vector<pair<int, int>>>(带权) 并查集 vector<int> parent 数组 字符串哈希映射 unordered_map<string, int> 滑动窗口最大值 deque<int> 实现单调队列 Top-K 问题 priority_queue<int>(小根堆维护 K 个最大) 动态排名查询 set<int> + distance(s.begin(), it)(但慢,可用 PBDS 替代) ❗常见陷阱提醒 错误 正确做法 map[key] 会创建默认值,可能改变结构 查找用 find() 避免插入 vector 迭代器在 push_back 后可能失效 使用索引或重新获取 erase(remove(...)) 忘记写法 vec.erase(std::remove(vec.begin(), vec.end(), x), vec.end()); priority_queue 无法遍历 若需遍历,改用 vector + make_heap 或排序 unordered_map 被哈希攻击 加随机种子或切换到 map 给出以上内容的对应代码,并且简要注释功能
最新发布
10-23
<think>我们讨论C++中的`priority_queue`,它是标准模板库(STL)中的一个容器适配器。它提供了一个最大堆(默认)数据结构,允许我们快速访问和删除队列中优先级最高的元素。###优先队列的常见用途1.**任务调度**:操作系统使用优先队列调度进程,优先级高的任务先执行。2.**图算法**:如Dijkstra算法中,需要快速获取当前最短路径节点。3.**数据流处理**:实时处理数据时,可能需要处理当前最重要的数据(如交易系统中价格最高的订单)。4.**模拟系统**:事件驱动的模拟,按时间顺序处理事件。5.**哈夫曼编码**:构建哈夫曼树时,需要不断从集合中取出权重最小的两个节点。###示例代码以下示例展示了`priority_queue`的基本操作:```cpp#include<iostream>#include<queue>#include<vector>#include<functional>//用于greaterusingnamespacestd;intmain(){//默认是最大堆,使用vector作为底层容器,less表示最大堆(队首元素最大)priority_queue<int>maxHeap;//创建一个最小堆priority_queue<int,vector<int>,greater<int>>minHeap;//最大堆示例cout<< "最大堆(默认):\n";maxHeap.push(30);maxHeap.push(100);maxHeap.push(25);maxHeap.push(40);while(!maxHeap.empty()){//访问并删除最大元素cout<< maxHeap.top()<< "";maxHeap.pop();}cout<< endl;//输出:100403025//最小堆示例cout<< "最小堆:\n";minHeap.push(30);minHeap.push(100);minHeap.push(25);minHeap.push(40);while(!minHeap.empty()){//访问并删除最小元素cout<< minHeap.top()<< "";minHeap.pop();}cout<< endl;//输出:253040100//自定义优先规则:假设我们有一个任务结构体structTask{intpriority;//优先级stringdesc;//任务描述//重载小于运算符,以便priority_queue可以比较//注意:在最大堆中,我们重载<,但在priority_queue中,比较是反的(因为堆顶是最大的,而默认用less)//具体来说:在最大堆中,如果a<b,则b的优先级更高(堆顶元素最大)booloperator<(constTask&other)const{//我们希望优先级数值大的在堆顶(最大堆),所以这里用小于//但注意:priority_queue中比较是“小于”比较,但堆顶是最大的元素,所以实际上是按小于的逆序(从大到小)returnpriority< other.priority;}//如果要最小堆,则使用大于比较,但需要改变比较函数对象};//使用自定义类型的最大堆priority_queue<Task>tasks;tasks.push({5,"Cleantheroom"});tasks.push({1,"Fixabug"});tasks.push({10,"Writedocumentation"});tasks.push({3,"Testthecode"});cout<< "按优先级处理任务(从高到低):\n";while(!tasks.empty()){Taskcurrent=tasks.top();tasks.pop();cout<< "优先级"<< current.priority<< ":"<< current.desc<< endl;}/*输出顺序:优先级10:Writedocumentation优先级5:Cleantheroom优先级3:Testthecode优先级1:Fixabug*/return0;}```###代码解释1.**声明优先队列**:-`priority_queue<int>maxHeap;`:创建一个整型的最大堆(默认)。-`priority_queue<int,vector<int>,greater<int>>minHeap;`:创建一个整型的最小堆,其中第二个参数是底层容器类型(通常为vector),第三个参数是比较函数(greater<int>表示最小堆)。2.**成员函数**:-`push()`:将元素加入优先队列。-`top()`:访问堆顶元素(即优先级最高的元素)。-`pop()`:删除堆顶元素。-`empty()`:检查队列是否为空。-`size()`:返回队列中元素的个数。3.**自定义类型**:-需要重载小于运算符(`operator<`)以定义排序规则(对于最大堆)。注意在最大堆中,我们定义`<`,实际上堆顶元素是最大的(即按照从大到小排序)。-如果想在自定义类型中使用最小堆,可以定义比较函数对象,并在声明优先队列时指定。例如:```cppstructTask{intpriority;stringdesc;//不重载运算符,而是定义外部比较函数};//自定义比较函数(函数对象)structCompareTask{booloperator()(constTask&a,constTask&b){//在最小堆中,我们希望优先级小的在堆顶,所以用大于比较//注意:这里与堆的机制有关:priority_queue中,如果我们希望a的优先级比b低(即b先出),则返回truereturna.priority>b.priority;}};priority_queue<Task,vector<Task>,CompareTask>minHeapTasks;```###注意事项-优先队列的底层容器默认是`vector`,但也可以使用`deque`(不常用)。-默认情况下,优先队列是最大堆,即堆顶元素最大。可以通过指定`greater`作为比较函数来创建最小堆。-当使用自定义类型时,一定要确保定义的比较规则符合需求。§§相关问题§§:1.如何用优先队列实现一个最小堆?2.优先队列的底层数据结构是什么?它如何保证操作的高效性?3.在自定义类型中,如何实现优先队列按照特定成员变量的升序或降序排列?4.优先队列与普通队列(queue)以及栈(stack)的主要区别是什么?</think> ### C++优先队列 (`priority_queue`) 的应用与示例 `priority_queue` 是 C++标准库中的容器适配器,它提供了一种基于**优先级**的队列实现(底层通常是堆数据结构)。与普通队列的 FIFO 规则不同,`priority_queue` 按照元素优先级(默认从高到低)处理元素。 #### 主要应用场景 1. **任务调度**:处理高优先级任务 2. **路径查找算法**:如 Dijkstra 最短路径算法 3. **实时系统**:处理紧急事件 4. **数据流处理**:处理最大/最小值的元素 5. **模拟系统**:事件驱动的模拟过程 --- ### 基本用法(默认最大堆) ```cpp #include <iostream> #include <queue> #include <vector> #include <functional> // for greater int main() { // 默认最大堆(降序) std::priority_queue<int> pq; pq.push(30); pq.push(100); pq.push(25); pq.push(40); std::cout << "降序输出(默认最大堆): "; while(!pq.empty()) { std::cout << pq.top() << " "; pq.pop(); // 每次弹出最大值 } // 输出: 100 40 30 25 // 创建最小堆(升序) std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap; minHeap.push(30); minHeap.push(100); minHeap.push(25); minHeap.push(40); std::cout << "\n升序输出(最小堆): "; while(!minHeap.empty()) { std::cout << minHeap.top() << " "; minHeap.pop(); // 每次弹出最小值 } // 输出: 25 30 40 100 return 0; } ``` --- ### 自定义数据结构优先级队列 ```cpp #include <iostream> #include <queue> #include <string> // 自定义数据结构 struct Task { int priority; // 优先级值(值越小优先级越高) std::string description; // 重载<运算符定义优先级规则(最小值优先) bool operator<(const Task& other) const { return priority > other.priority; // 注意:这里使用>实现最小值优先 } }; int main() { // 优先级队列(最小值优先) std::priority_queue<Task> taskQueue; // 添加不同优先级的任务 taskQueue.push({3, "普通更新任务"}); taskQueue.push({1, "紧急修复BUG"}); taskQueue.push({2, "中等重要性需求"}); taskQueue.push({5, "后台日志清理"}); // 按优先级处理任务 std::cout << "\n任务处理顺序(值越小优先级越高):\n"; while(!taskQueue.empty()) { Task current = taskQueue.top(); std::cout << "优先级[" << current.priority << "]: " << current.description << "\n"; taskQueue.pop(); } /* 输出顺序: 优先级[1]: 紧急修复BUG 优先级[2]: 中等重要性需求 优先级[3]: 普通更新任务 优先级[5]: 后台日志清理 */ return 0; } ``` --- ### 实际应用:Dijkstra最短路径算法 ```cpp #include <iostream> #include <vector> #include <queue> #include <climits> // 使用优先队列优化Dijkstra算法 void dijkstra(const std::vector<std::vector<std::pair<int, int>>>& graph, int source) { int n = graph.size(); std::vector<int> dist(n, INT_MAX); dist[source] = 0; // 最小堆:储(距离, 节点)对 using Node = std::pair<int, int>; std::priority_queue<Node, std::vector<Node>, std::greater<Node>> pq; pq.push({0, source}); while(!pq.empty()) { auto [d, u] = pq.top(); // C++17结构化绑定 pq.pop(); if(d != dist[u]) continue; // 跳过过时信息 for(const auto& [v, weight] : graph[u]) { int newDist = dist[u] + weight; if(newDist < dist[v]) { dist[v] = newDist; pq.push({newDist, v}); } } } // 输出结果 std::cout << "从节点" << source << "到各点的最短距离:\n"; for(int i = 0; i < n; ++i) { std::cout << "节点" << i << ": " << dist[i] << "\n"; } } int main() { // 邻接表表示图:{目标节点, 权重} std::vector<std::vector<std::pair<int, int>>> graph = { {{1, 4}, {2, 1}}, // 节点0 -> 1(4), 2(1) {{3, 2}}, // 节点1 -> 3(2) {{1, 2}, {3, 5}}, // 节点2 -> 1(2), 3(5) {{4, 3}}, // 节点3 -> 4(3) {} // 节点4 }; dijkstra(graph, 0); } ``` --- ### 性能说明 1. **时间复杂度**: - 插入元素:O(log n) - 删除顶部元素:O(log n) - 访问顶部元素:O(1) 2. **底层实现**:默认基于`vector`实现二叉堆 3. **适用场景**: - 需要频繁访问最大/最小元素 - 处理动态变化的优先级数据 - 不适合需要随机访问的场合
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值