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

一、什么是优先队列?

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

普通队列 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),一旦题目出现"反复取最值",基本就可以考虑它。

相关推荐
智者知已应修善业1 分钟前
【51单片机8位数码管同时倒计时从9999】2024-1-25
c++·经验分享·笔记·算法·51单片机
洛水水4 分钟前
【力扣100题】86.柱状图中最大的矩形
算法·leetcode·职场和发展
渡之11 分钟前
GRiM-Net 深度解析 | 无人机 GNSS 拒止场景下两阶段跨视角视觉定位框架
深度学习·算法·动态规划·无人机
测试仪器廖生1359025638530 分钟前
罗德与施瓦茨 FSP13频谱分析仪FSP30
网络·人工智能·算法
happymaker062633 分钟前
LeetCodeHot100——560.和为K的子数组
算法
dtq04241 小时前
C语言刷题数组5,6(求平均值,求最大值)
c语言·数据结构·算法
郭梧悠1 小时前
Hash算法入门Hash冲突解决方案
算法·哈希算法
洛水水2 小时前
【力扣100题】81.寻找两个正序数组的中位数
数据结构·算法·leetcode
happymaker06262 小时前
LeetCodeHot100——155.最小栈
算法
洛水水2 小时前
【力扣100题】85.每日温度
算法·leetcode·职场和发展