当前位置: 首页 > news >正文

[数据结构与算法] 优先队列 | 最小堆 C++

下面是关于 C++ 中 std::priority_queue 的详细说明,包括初始化、用法和常见的应用场景。


什么是 priority_queue

priority_queue(优先队列)是 C++ 标准库中的一个容器适配器。它和普通队列(queue)最大的不同在于,元素的出队顺序不是根据它们入队的顺序(先进先出),而是根据它们的优先级

默认情况下,priority_queue 是一个大顶堆(Max-Heap),意味着优先级最高的元素是值最大的元素。每次调用 top() 都会返回当前队列中最大的元素。

  • 底层实现:通常使用**堆(Heap)**数据结构来实现,这使得它能以对数时间复杂度(O(log n))插入元素和删除最大(或最小)的元素。
  • 头文件:使用前需要包含头文件 <queue>
#include <iostream>
#include <queue>
#include <vector>
#include <functional> // 包含 std::greater 和 std::less

1. priority_queue 的初始化

priority_queue 的模板定义如下:

template <class T, class Container = std::vector<T>, class Compare = std::less<typename Container::value_type>>
class priority_queue;
  • T: 存储的元素类型。
  • Container: 用于存储元素的底层容器,默认为 std::vector<T>。必须是支持随机访问迭代器和 front(), push_back(), pop_back() 等操作的序列容器,例如 std::vectorstd::deque
  • Compare: 比较函数或函数对象,用于确定元素的优先级。默认为 std::less<T>,它会创建一个大顶堆。
1.1 默认初始化(大顶堆)

最常见的用法,创建一个存储 int 类型的大顶堆。每次 top() 都会返回最大的元素。

// 默认情况下,是 std::vector 作为容器,std::less 作为比较函数,即大顶堆
std::priority_queue<int> max_heap;max_heap.push(30);
max_heap.push(100);
max_heap.push(25);
max_heap.push(40);// 此刻队列顶部的元素是 100
std::cout << "Top element is: " << max_heap.top() << std::endl; // 输出 100
1.2 初始化为小顶堆

如果你想让 top() 总是返回最小的元素,你需要创建一个小顶堆(Min-Heap)。这需要显式提供所有模板参数:

  • T: 元素类型。
  • Container: 底层容器,通常是 std::vector<T>
  • Compare: 比较函数,使用 std::greater<T>
// 使用 std::greater<int> 来创建小顶堆
std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap;min_heap.push(30);
min_heap.push(100);
min_heap.push(25);
min_heap.push(40);// 此刻队列顶部的元素是 25
std::cout << "Top element is: " << min_heap.top() << std::endl; // 输出 25
1.3 存储自定义结构体

当存储自定义类型(如 structclass)时,你需要告诉优先队列如何比较它们。有两种主要方法:

方法一:重载 < 运算符(用于大顶堆)

struct Node {int id;int priority;// 重载 < 运算符,priority 越大,优先级越高bool operator<(const Node& other) const {return this->priority < other.priority;}
};std::priority_queue<Node> pq;
pq.push({1, 10});
pq.push({2, 5});
pq.push({3, 20});// top() 将返回 priority 最大的 Node,即 {3, 20}
std::cout << "Top node: id=" << pq.top().id << ", priority=" << pq.top().priority << std::endl;

方法二:提供自定义比较器(更灵活)

如果你不想或不能修改结构体定义,或者需要多种排序方式,可以提供一个自定义的比较函数对象(Functor)。

struct Node {int id;int priority;
};// 自定义比较器结构体
struct CompareNode {// 返回 true 表示 a 的优先级低于 bbool operator()(const Node& a, const Node& b) {// 创建小顶堆,priority 越小,优先级越高return a.priority > b.priority;}
};std::priority_queue<Node, std::vector<Node>, CompareNode> pq;
pq.push({1, 10});
pq.push({2, 5});
pq.push({3, 20});// top() 将返回 priority 最小的 Node,即 {2, 5}
std::cout << "Top node: id=" << pq.top().id << ", priority=" << pq.top().priority << std::endl;

使用 Lambda 表达式(C++11及以上)也可以实现,但定义比较器时需要 decltype

1.4 从已有容器初始化

你可以用一个已有的容器(如 vector)中的元素来初始化优先队列。

std::vector<int> data = {30, 100, 25, 40};// 从 data 初始化一个大顶堆
std::priority_queue<int> max_heap_from_vec(data.begin(), data.end());std::cout << "Top element from vector init: " << max_heap_from_vec.top() << std::endl; // 输出 100// 初始化小顶堆
std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap_from_vec(data.begin(), data.end());
std::cout << "Top element from vector init (min-heap): " << min_heap_from_vec.top() << std::endl; // 输出 25

2. priority_queue 的常用操作

优先队列的操作非常简单直观:

  • push(element): 向队列中插入一个元素。时间复杂度 O(log n)。
  • pop(): 移除队首(优先级最高)的元素。时间复杂度 O(log n)。
  • top(): 返回队首(优先级最高)元素的常引用,不删除元素。时间复杂度 O(1)。
  • empty(): 检查队列是否为空。
  • size(): 返回队列中元素的数量。

示例代码:

std::priority_queue<int> pq;std::cout << "Is queue empty? " << (pq.empty() ? "Yes" : "No") << std::endl;pq.push(10);
pq.push(50);
pq.push(20);std::cout << "Queue size: " << pq.size() << std::endl;
std::cout << "Top element: " << pq.top() << std::endl; // 50pq.pop(); // 移除了 50std::cout << "Top element after pop: " << pq.top() << std::endl; // 20// 遍历并清空优先队列
std::cout << "Queue elements in priority order: ";
while (!pq.empty()) {std::cout << pq.top() << " ";pq.pop();
}
std::cout << std::endl;
// 输出: Queue elements in priority order: 20 10

注意priority_queue 没有提供迭代器,不能像 vector 那样直接遍历。唯一的访问方式就是通过 top() 获取队首元素。


3. priority_queue 的应用场景

优先队列在算法和系统设计中非常有用,尤其适合处理那些需要动态维护“最大/最小”元素的问题。

3.1 Top-K 问题

这是最经典的应用。例如,从一个巨大的数据流中找出最大的 K 个元素。

思路

  • 维护一个大小为 K 的小顶堆
  • 遍历数据流,对于每个新元素:
    • 如果堆的大小小于 K,直接将元素插入堆中。
    • 如果堆的大小等于 K,将新元素与堆顶元素(当前 K 个元素中最小的那个)比较。
    • 如果新元素大于堆顶元素,则弹出堆顶,并将新元素插入。
  • 遍历结束后,堆中剩下的 K 个元素就是最大的 K 个元素。

为什么用小顶堆? 因为小顶堆的堆顶是“门槛”,只有比这个门槛大的元素才有资格进入这个“Top-K 圈子”,这样可以高效地淘汰掉不够大的元素。

3.2 堆排序(Heap Sort)

虽然 C++ 提供了 std::sort,但优先队列可以很自然地实现堆排序。

  1. 将所有元素插入一个大顶堆。
  2. 依次从堆中取出堆顶元素,放入结果数组中。取出的顺序就是从大到小。
3.3 Dijkstra 算法

在图论中,Dijkstra 算法用于寻找图中单源最短路径。算法的核心是每次都从“待处理”的顶点中,选择一个距离源点最近的顶点进行扩展。优先队列(小顶堆)可以完美地胜任这个任务。

  • 优先队列中存储 pair<int, int>,即 {距离, 顶点编号}
  • 每次从队列中取出 top(),就是当前距离源点最近的未访问顶点。
3.4 Huffman 编码

在数据压缩中,Huffman 编码算法使用优先队列来构建最优二叉树(Huffman 树)。

  • 将每个字符及其频率作为一个节点放入一个优先队列(小顶堆)中,频率越小,优先级越高。
  • 每次从队列中取出两个频率最小的节点,合并成一个新的父节点(频率为两者之和),再将新节点放回优先队列。
  • 重复此过程,直到队列中只剩一个节点,即 Huffman 树的根。
3.5 任务调度系统

在操作系统或中间件中,任务调度器需要从多个待执行的任务中选择一个优先级最高的来执行。优先队列可以用来管理这些任务,top() 总是返回当前最紧急的任务。


希望这份详细的说明对你有帮助!

http://www.xdnf.cn/news/1098901.html

相关文章:

  • C语言——预处理详解
  • Swift 图论实战:DFS 算法解锁 LeetCode 323 连通分量个数
  • 第一次搭建数据库
  • 【macos用镜像站体验】Claude Code入门使用教程和常用命令
  • B2、进度汇报(— 25/06/16)
  • 【Python进阶篇 面向对象程序设计(7) Python操作数据库】
  • Duplicate cleaner pro 的使用技巧
  • 专题:2025供应链数智化与效率提升报告|附100+份报告PDF、原数据表汇总下载
  • 【fitz+PIL】PDF图片文字颜色加深
  • 阿里云错题集分享
  • linux-MySQL的安装
  • Centos 7下使用C++使用Rdkafka库实现生产者消费者
  • 介绍 cnpm exec electron-packager
  • Kafka的无消息丢失配置怎么实现
  • Chromium 引擎启用 Skia Graphite后性能飙升
  • 在徐州网络中服务器租用与托管的优势
  • 机器学习13——支持向量机下
  • 大数据时代UI前端的智能化升级:基于机器学习的用户意图预测
  • Qt开发:QtConcurrent介绍和使用
  • RocksDB 与 ZenFS:原理、特性及在科研与工程中的应用初步探索
  • 配置双网卡Linux主机作为路由器(连接NAT网络和仅主机模式网络)
  • systemd服务脚本详解与管理命令
  • vue3 td 标签优化时间显示
  • LFU 缓存
  • 【笔记分享】集合的基数、群、环、域
  • QT解析文本框数据——概述
  • 实现一个点击输入框可以弹出的数字软键盘控件 qt 5.12
  • 文件系统子系统 · 核心问题问答精要
  • 【性能测试】jmeter+Linux环境部署和分布式压测,一篇打通...
  • 动态规划疑惑总结