news 2026/10/14 19:35:16

【C++】 priority_queue知识点总结

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【C++】 priority_queue知识点总结

一、基础概念

priority_queue叫优先队列,底层不是队列,是堆(大顶堆 / 小顶堆),属于容器适配器。

适配器:不自己实现底层存储,复用别的容器(vector/deque)做底层,只在外层封装堆操作。 队列:先进先出;优先队列:优先级最高的元素先出,和入队顺序无关。

头文件:#include <queue>

// STL原型 template <class T, class Container = vector<T>, class Compare = less<T>> class priority_queue;

三个模板参数:

  1. T:存储元素类型
  2. Container:底层容器,默认vector<T>,要求支持[]、push_back、pop_back
  3. Compare:仿函数(二元谓词),默认less<T>,决定优先级规则
  4. ⚠️ 最重要规则(和你手写仿函数完全一致):comp(a,b)返回 true →a 的优先级低于 b,b 要排在 a 前面
  5. 大顶堆要求:父节点应该大于等于子节点,当子节点 > 父节点的时候才交换。
    默认传小于Less仿函数,大的优先级高
    传大于Greater仿函数,小的优先级高

二、默认大顶堆原理:priority_queue<int>

默认:priority_queue<int, vector<int>, less<int>>less<int>:comp(a,b)=a < b

  • comp(parent, child)→parent < child返回 true:父优先级 < 子,子上浮
  • 堆顶是最大元素→大顶堆

小顶堆写法:

priority_queue<int, vector<int>, greater<int>> minHeap;

greater<int>:comp(a,b)=a > b

堆顶是最小元素。

面试高频坑:less→ 大顶堆;greater→ 小顶堆,很多人记反!

三、底层堆原理(二叉堆,完全二叉树,数组存储)

用数组映射完全二叉树:

  • 父节点下标:parent
  • 左孩子:2*parent +1
  • 右孩子:2*parent +2
  • 子节点找父:(child - 1)/2(整数除法向下取整)

堆的两个核心操作:

  1. 向上调整 adjust_up(插入 push)新元素插在数组末尾,不断和父节点比较;如果子优先级更高,交换父子,直到不满足条件 / 到根。

    对应 push:push_back+adjust_up(最后一个下标)

  2. 向下调整 adjust_down(删除 pop)堆顶和末尾元素交换,pop_back删掉堆顶;新堆顶向下不断和优先级更高的孩子交换,直到满足堆性质。

    对应 pop:首尾 swap + pop_back + adjust_down (0)

  1. 建堆(迭代器区间构造)从最后一个非叶子节点开始,向前依次向下调整。 最后一个非叶子节点下标:(n-2)/2

    时间复杂度:O (n),不是 O (nlogn)!

插入单个元素:O (logn);删除堆顶:O (logn);建堆:O (n)

四、priority_queue 常用接口

接口作用注意事项
push(x)插入元素,自动调整堆底层 push_back + 向上调整
pop()删除堆顶(优先级最高元素)不返回堆顶元素!空队列调用 UB
top()获取堆顶元素的引用空队列调用 UB;const 引用
empty()判空bool
size()返回元素个数size_t

queue 的 front () 返回队首并 pop 分开;priority_queuepop()只删不取,top()只取不删。

#include <queue> int main() { priority_queue<int> max_pq; //大顶堆 max_pq.push(1); max_pq.push(9); max_pq.push(5); while(!max_pq.empty()) { cout << max_pq.top() << " "; max_pq.pop(); } //输出:9 5 1 return 0; }

五、仿函数 Compare 深度理解

Compare comp是二元谓词仿函数,comp(a,b)返回 bool:

comp(a,b) == true :a 优先级低于 b,b 应该放在 a 上方(堆中 b 优先级更高)

// less<T> bool operator()(const T& a,const T& b) { return a < b; }

comp(p,c)→p < c→ true:父 p 优先级低于子 c → 交换上浮,大顶堆。

// greater<T> bool operator()(const T& a,const T& b) { return a > b; }

comp(p,c)→p>c→ true:父 p 优先级低于子 c → 交换上浮,小顶堆。

记忆口诀:less是拿<比较,大顶堆greater拿>比较,小顶堆

六、容器适配器

一、什么是容器适配器

容器适配器(Container Adapter)本身不是独立容器,它是对已有容器的封装。适配器不自己开辟内存、不实现底层存储,复用底层容器的已有接口,对外只暴露一套新的、简化的接口,改变容器的行为。

一句话理解:适配器 = 套壳包装。底层容器负责存数据;适配器只提供特定访问规则。

一句话理解:适配器 = 套壳包装。底层容器负责存数据;适配器只提供特定访问规则。

STL 一共 3 个容器适配器:

  1. stack栈:后进先出 LIFO
  2. queue队列:先进先出 FIFO
  3. priority_queue优先队列:优先级最高元素先出(堆)
适配器默认底层容器底层容器要求
stackdeque<T>支持push_back/pop_back/back()
queuedeque<T>支持push_back/pop_front/back()/front()
priority_queuevector<T>支持随机访问[]、push_back、pop_back(堆算法需要随机下标访问)

适配器只是限制接口,不会新增存储能力。

二、三大适配器模板原型

1. stack

template< class T, class Container = deque<T> > class stack;

对外接口:push()、pop()、top()、empty()、size()

只能在尾部操作:入栈、出栈、取栈顶,不能遍历、不能访问中间元素

2. queue

template< class T, class Container = deque<T> > class queue;

对外接口:push()、pop()、front()、back()、empty()、size()

尾插、头删;只能取队首队尾,不能遍历中间元素

3. priority_queue

template< class T, class Container = vector<T>, class Compare = less<T> > class priority_queue;

对外接口:push()、pop()、top()、empty()、size()

只允许取堆顶;push/pop 内部自动做堆调整。

三、底层容器更换规则

适配器可以手动更换底层容器,但必须满足接口要求

stack

底层可以用:deque/vector/list只要支持尾插尾删、back ()

stack<int, vector<int>> st;

queue

底层不能用 vector! queue 需要头删pop_front(),vector 的头删效率极低,而且 STL 不允许 vector 作为 queue 底层。 可选:deque、list

queue<int, list<int>> q;

priority_queue

必须支持随机访问 [],堆调整需要按下标找父子节点 ✅ 允许:vector、deque❌ 禁止:list(没有随机访问)

priority_queue<int, deque<int>> pq;

面试题:为什么 priority_queue 默认是 vector,stack/queue 默认 deque? 答:

  1. vector 连续内存,随机访问快,最适合堆的下标操作;
  2. deque 头尾增删都是 O (1),不需要移动大量元素,适合 stack、queue。

四、适配器的共性特点

  1. 没有迭代器!不能遍历适配器刻意隐藏底层容器迭代器。stack/queue/priority_queue 不提供 begin () /end ()。

设计目的:强制遵守数据结构规则。栈就只能栈顶操作,队列只能头尾操作,禁止乱序遍历。 如果你拿到迭代器,就可以随便修改中间元素,破坏栈 / 队列 / 堆的性质。

  1. 底层容器是适配器的成员就像你手写 priority_queue 里面:Container _con;,所有操作都是调用_con的成员函数。
  2. 五、适配器对比表

    表格

    特性stackqueuepriority_queue
    规则LIFO 后进先出FIFO 先进先出按优先级出队
    默认底层dequedequevector
    插入位置尾部 push_back尾部 push_back尾部 push_back + 向上堆调整
    删除位置尾部 pop_back头部 pop_front首尾交换 + pop_back + 向下堆调整
    取元素top () 栈顶front () 队首 back () 队尾top () 堆顶
    迭代器❌无❌无❌无
    模板参数T, ContainerT, ContainerT, Container, Compare 仿函数

七、仿函数

仿函数本质:重载了operator()的类 / 结构体对象,可以像函数一样去调用。也叫函数对象。

1. 核心原理

普通函数调用:func(a,b)仿函数调用:obj(a,b)

看着像函数调用,实际是调用这个对象里重载的operator()

template<class T> class Less { public: bool operator()(const T& x, const T& y) { return x < y; } };
  • Less<int>是类
  • Less<int>()是临时对象(匿名仿函数)
  • com(a,b)等价com.operator()(a,b)
Less<int> lessObj; // 创建仿函数对象 cout << lessObj(2,5); // 等价 lessObj.operator()(2,5),返回 true

2. 仿函数 vs 普通函数

表格

仿函数(类重载 operator ())普通函数
状态可以保存成员变量,携带状态不能存状态
模板容易和 STL 算法配合,类型推导友好函数指针类型推导麻烦
内联编译器更容易内联,效率高容易退化成函数调用

写的Less/Greater就是 STL 风格的谓词仿函数(返回 bool,两个参数 → 二元谓词)

3. 二元谓词规则

com(x,y)返回true的含义:

x 应该排在 y 的后面(y 优先级更高,y 放前面)

Less<int> com; com(x,y) → x < y 返回true // 含义:x比y小,x优先级更低,y更大,y要放到前面 → 降序排序
Greater<int> com; com(x,y) → x > y 返回true // 含义:x比y大,x优先级更低,y更小,y放到前面 → 升序排序

4. 仿函数两种使用方式

方式 1:匿名临时对象(你 main 里用的)

BubbleSort(a,7, Less<int>()); // Less<int>() 创建一个临时对象,传给模板参数Compare

方式 2:先定义对象,再传入

Less<int> lessObj; BubbleSort(a,7, lessObj);

5. 仿函数可以带状态

举个例子:统计比较次数

template<class T> class LessCount { public: int count = 0; bool operator()(const T& x, const T& y) { count++; // 记录调用次数 return x < y; } };

手搓代码:

#pragma once #include<iostream> #include<vector> #include<assert.h> using namespace std; template<class T> class Less { public: bool operator()(const T& x, const T& y) { return x < y; } }; template <class T> class Greater { public: bool operator()(const T& x, const T& y) { return x > y; } }; namespace cl { template <class T,class Container = vector<T>,class Compare = Less<T>> class priority_queue { public: priority_queue() = default;//强制编译器生成默认构造 //用一段迭代器区间去进行初始化 template <class InputIterator> priority_queue(InputIterator first, InputIterator last) :_con(first,last) { //向下调整建堆 //向下调整建堆是从最后一个非叶子节点开始,即(_cin.size()-1-1)/2 这是一个父节点 for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--) { adjust_down(i); } } void push(const T& x) { _con.push_back(x); adjust_up(_con.size() - 1); } void pop() { assert(!empty()); swap(_con[0], _con[_con.size() - 1]); _con.pop_back(); adjust_down(0); } const T& top() { assert(!empty()); return _con[0]; } bool empty() const { return _con.empty(); } size_t size() const { return _con.size(); } private: void adjust_up(int child) { Compare com; int parent = (child - 1) / 2; while (child > 0) { //if (_con[child] > _con[parent]) //if(_con[parent]<_con[child]) if (com(_con[parent], _con[child])) { swap(_con[child], _con[parent]); child = parent; parent = (child - 1)/2; } else { break; } } } void adjust_down(int parent) { //建立一个仿函数对象 Compare com; size_t child = 2 * parent + 1; while(child < _con.size()) { // 如果右孩子存在,并且右孩子比左孩子更满足比较规则,则child++选右孩子 //if (child + 1 < _con.size() && _con[child] < _con[child + 1]) if (child + 1 < _con.size() && com(_con[child], _con[child + 1])) ++child; //if (_con[child] > _con[parent]) if (com(_con[parent], _con[child])) { swap(_con[child], _con[parent]); parent = child; child = 2 * parent + 1; } else break; } } private: Container _con; }; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 19:34:37

CPU中断机制实战:从硬件信号到eBPF观测

1. 这不是教科书里的“CPU和中断”&#xff0c;而是我拆了三台服务器、重装过十七次内核后才敢写的实操笔记你点开这个标题&#xff0c;大概率正被“中断响应时间抖动”卡在项目验收前夜&#xff0c;或是刚在嵌入式调试中看到一串无法复位的IRQ异常&#xff0c;又或者——你只是…

作者头像 李华
网站建设 2026/10/14 19:34:26

Android录屏直播推流全解析:MediaProjection采集到RTMP发送

简介&#xff1a;面向中高级 Android 开发者的录屏直播实战方案&#xff0c;用于解决在移动端完成屏幕画面与音频同步采集、编码并通过 RTMP 协议推送到流媒体服务器的完整链路问题&#xff1b;资源围绕 Android 原生技术展开&#xff0c;涵盖 MediaProjection 录屏、AudioReco…

作者头像 李华
网站建设 2026/10/14 19:34:20

plugins插件加载失败:根因排查与系统设计指南

先说个实话&#xff1a;plugins 这个单词看起来简单&#xff0c;但几乎每个和工程打过交道的人都在它身上浪费过至少一下午。要么是 IAR 里装了个插件没反应&#xff0c;要么是构建工具启动时直接抛 failed to load plugins web boot&#xff0c;要么是测试 harness 组件加载失…

作者头像 李华
网站建设 2026/10/14 19:34:19

box-shadow 属性值详解

语法 box-shadow: offset-x offset-y blur-radius spread-radius color inset;只有 offset-x 和 offset-y 是必填的&#xff0c;其余参数可选&#xff0c;且顺序不能乱。 参数说明参数是否必填说明offset-x必填水平偏移量&#xff0c;正值向右&#xff0c;负值向左offset-y必填…

作者头像 李华
网站建设 2026/10/14 19:34:02

OpenShell:一套配置统一管理 zsh/bash/fish 终端环境

1. 项目背景与整体设计思路1.1 为什么会有 OpenShell 这个项目先说说我自己的痛点。做开发这些年&#xff0c;我在本机和服务器上来回切换&#xff0c;用的是不同的 shell&#xff1a;本地 macOS 用 zsh&#xff0c;Ubuntu 服务器上是 bash&#xff0c;偶尔还要在 Windows 的 G…

作者头像 李华
网站建设 2026/10/14 19:34:01

Python港股实时行情API接入:恒生股票数据获取与AH股溢价监控实战

做跨境量化或者港股通策略的开发者&#xff0c;接港股数据是绕不开的。港股跟A股有个很特别的联动关系——同一家公司在A股和港股两地上市&#xff0c;价格经常不一样&#xff0c;这就是所谓的AH股溢价。这个价差不仅是金融从业者关心的套利机会&#xff0c;也是技术开发者做跨…

作者头像 李华