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

一、什么是优先队列?

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

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

相关推荐
刀法如飞37 分钟前
Go 字符串查找的 20 种实现方式,用不同思路解决问题
算法·面试·程序员
Dlrb12112 小时前
C语言-指针数组与数组指针
c语言·数据结构·算法·指针·数组指针·指针数组·二级指针
WL_Aurora2 小时前
Python 算法基础篇之集合
python·算法
平行侠3 小时前
A15 工业路由器IP前缀高速检索与内存压缩系统
网络·tcp/ip·算法
阿旭超级学得完4 小时前
C++11包装器(function和bind)
java·开发语言·c++·算法·哈希算法·散列表
li星野4 小时前
位运算 & 数学 & 高频进阶九题通关(Python + C++)
c++·python·学习·算法
jerryinwuhan4 小时前
hello算法,简单讲(1)
算法·排序算法
y = xⁿ4 小时前
20天速通LeetCodeday15:BFS广度优先搜索
算法·宽度优先
400分4 小时前
吃透RAG核心-----语义检索与关键字检索底层原理
算法·架构
目黑live +wacyltd4 小时前
算法备案:常见驳回原因与应对策略
人工智能·算法