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

一、什么是优先队列?

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

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

相关推荐
QXWZ_IA4 小时前
1库1图1批是什么?千寻位置公安地图数据体系详解
科技·算法·能源·媒体·交通物流·政务
c238564 小时前
Bug 猎手入门指南
c++·算法·bug
Reart5 小时前
Leetcode 213.打家劫舍2(内含闲谈,打劫真是技术活,好题,716)
后端·算法
Reart6 小时前
Leetcode 198.打家劫舍(716)
后端·算法
Jerry6 小时前
LeetCode 110. 平衡二叉树
算法
玖玥拾6 小时前
C++ 数据结构 八大基础排序算法专题
数据结构·c++·算法·排序算法
Tim_106 小时前
【C++】017、new/delete与malloc/free的区别
java·数据结构·算法
从零开始的代码生活_7 小时前
C++ list 原理与实践:双向链表、迭代器与简化实现
开发语言·c++·后端·学习·算法·链表·list
ttod_qzstudio8 小时前
【软考算法】软件设计师下午第四题之动态规划:0-1 背包与最长公共子序列的“填表艺术“
算法·动态规划·软考