【数据结构与算法】优先队列

一、什么是优先队列?

优先队列就是一个会自动排序的队列。

普通队列 vs 优先队列

cpp 复制代码
// 普通队列:先进先出
queue<int> q;
q.push(3);
q.push(1);
q.push(2);
// 取出顺序:3, 1, 2

// 优先队列:按大小排序
priority_queue<int> pq;
pq.push(3);
pq.push(1);
pq.push(2);
// 取出顺序:3, 2, 1(从大到小)

优先队列的基本用法

1. 头文件

cpp 复制代码
#include <queue>

2. 声明方式

cpp 复制代码
// 方式1:最大堆(默认)
priority_queue<int> pq1;  // 最大的元素在顶部

// 方式2:最小堆
priority_queue<int, vector<int>, greater<int>> pq2;  // 最小的元素在顶部

// 方式3:自定义比较
priority_queue<Node*, vector<Node*>, Compare> pq3;  // 按自定义规则排序

三、常用操作

cpp 复制代码
priority_queue<int> pq;

// 添加元素
pq.push(10);
pq.push(5);
pq.push(20);

// 访问顶部元素
int top = pq.top();  // 返回20(最大值)

// 删除顶部元素
pq.pop();  // 删除20

// 判断是否为空
if (pq.empty()) { }

// 获取大小
int size = pq.size();  // 2

四、三种声明方式详解

方式1:最大堆(默认)

cpp 复制代码
priority_queue<int> pq;
// 等价于
priority_queue<int, vector<int>, less<int>> pq;

特点: 最大的元素在顶部

cpp 复制代码
pq.push(3);
pq.push(1);
pq.push(2);
cout << pq.top();  // 输出 3

方式2:最小堆

cpp 复制代码
priority_queue<int, vector<int>, greater<int>> pq;

特点: 最小的元素在顶部

cpp 复制代码
pq.push(3);
pq.push(1);
pq.push(2);
cout << pq.top();  // 输出 1

方式3:自定义类型

cpp 复制代码
struct Node {
    int weight;
    int ascii;
};

struct Compare {
    bool operator()(Node a, Node b) {
        // 返回true表示a的优先级低于b
        if (a.weight != b.weight) {
            return a.weight > b.weight;  // 权值小的优先
        }
        return a.ascii > b.ascii;  // ASCII小的优先
    }
};

priority_queue<Node, vector<Node>, Compare> pq;

优先队列本质上就是一种"每次都能快速取出最大或最小元素"的数据结构,你可以把它理解成一个"自动排序的队列",但它不是完全排序的,而是始终保证队头元素是当前优先级最高的那个;在 C++ 里,默认是大根堆,也就是每次取出最大值。

先说最核心的特点;普通队列是先进先出,而优先队列是"优先级高的先出",至于谁优先级高,是由比较规则决定的;底层实现一般是堆(heap),所以插入和删除的时间复杂度都是 O(log n),查询堆顶是 O(1)。

C++ 中的基本用法很固定;定义一个优先队列 priority_queue pq,这样默认就是大根堆;如果你想要小根堆,就要写成 priority_queue<int, vector, greater> pq;这个写法的意思是用 vector 存数据,用 greater 作为比较函数,让小的优先出来。

常用操作就几个;push(x) 把元素加入队列;pop() 删除队头元素(注意不会返回值);top() 查看当前队头;empty() 判断是否为空;size() 查看元素个数;这些操作和普通队列类似,但语义不同。

再说一个非常常见的用法,就是存 pair;比如在 Dijkstra 里,我们通常存 pair<距离, 节点编号>,并且用小根堆,这样每次取出的都是当前距离最小的点;写法是 priority_queue<pair<long long,int>, vector<pair<long long,int>>, greater<pair<long long,int>>> pq;这里的比较是先比较 first,如果相等再比较 second。

优先队列最经典的应用有几个;第一是最短路径(Dijkstra),每次取当前距离最小的点;第二是哈夫曼树(合并果子),每次取最小的两个数;第三是一些"动态维护最大/最小值"的问题,比如不断加入元素并随时查询最大值。

最后讲一个容易踩的坑;priority_queue 不能像 vector 一样遍历,它不支持随机访问,如果你想看所有元素,只能不断 pop;另外,pop 只删除元素,不返回值,一定要先 top 再 pop;还有一点就是,如果你自定义结构体,一定要写比较规则,否则编译会报错。

总结一句:优先队列就是一个"随时能拿到当前最优元素"的工具,核心是堆结构,时间复杂度 O(log n),一旦题目出现"反复取最值",基本就可以考虑它。

相关推荐
fpcc几秒前
算法和数据结构—动态规划法
数据结构·算法·动态规划
星星.7222 小时前
C++算法竞赛|二分查找与二分答案:边界模板、浮点二分、STL
数据结构·c++·算法
sel_92 小时前
【OPD论文导读(二)】OPD(On-Policy Distillation)全景调研:十篇论文讲透“在自己生成的内容上学习“这件事
人工智能·python·深度学习·学习·算法·语言模型
动词ing3 小时前
【学习笔记】数据结构(数组长度和关键特性+快慢指针+左右指针)
数据结构·笔记·学习
金幄科技3 小时前
RAG第四篇:重排序 Rerank——粗召回之后,为什么还需要一次精排
人工智能·算法·ai·ai编程
M78佐菲3 小时前
Linux学习笔记:文件IO
linux·笔记·学习·算法
lhldsg4 小时前
课程排课系统实战指南:从数据库设计到算法调优全流程解析
java·数据库·算法·小程序
BSD_HY4 小时前
薄膜开关矩阵扫描电路设计中的防鬼键措施
人工智能·算法·矩阵·人机交互·薄膜开关·源头工厂·深圳工厂
linux-hzh4 小时前
百日算法修炼 · Day 17
数据结构·算法
XUEYUAN52124 小时前
代理日志分析与监控:代理池健康状态巡检与分级告警体系搭建(运维实战)
运维·网络·网络协议·tcp/ip·算法·架构