【C++初阶】模拟实现优先级队列priority_queue

👦个人主页:@Weraphael
✍🏻作者简介:目前学习C++和算法
✈️专栏:C++航路
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注✨
目录
- 一、priority_queue的介绍
- 二、为什么priority_queue不像stack和queue一样使用deque作为其底层存储数据的容器呢
- 三、priority_queue的常见操作
- 四、模拟实现priority_queue
- 4.1 构造函数
- 4.2 删除堆顶元素pop
- 4.3 插入push
- 4.4 获取堆顶元素top
- 4.5 判断是否为空empty
- 4.6 获取个数大小size
- 五、仿函数
- 5.1 什么是仿函数
- 5.2 实现priority_queue的仿函数
一、priority_queue的介绍

priority_queue是一个容器适配器,默认使用vector作为其底层存储数据的容器priority_queue在vector上使用了堆heap的算法将vector中元素构造堆的结构,因此,priority_queue就是堆。默认情况下是大堆。(如何构造成小堆在【仿函数】会讲解到)
二、为什么priority_queue不像stack和queue一样使用deque作为其底层存储数据的容器呢
- 这是因为
vector在插入和删除元素时具有较好的性能表现。
在堆中插入新元素时,为了保持堆的特性(大堆/小堆)。则需要通过不断地比较和移动元素来完成。vector作为一个连续的内存块,可以更高效地进行元素的插入操作。
相比之下,deque在插入和删除操作时,需要考虑在数组的两端进行操作(头部和尾部),并且要进行元素的移动操作,使得时间复杂度稍高于vector。
尽管deque在头部和尾部插入/删除操作上有一些优势,但对于优先级队列这种需要频繁访问和处理最高优先级元素的场景来说,vector更加合适
-
弹出
pop元素:若要得到堆顶的元素,需要将堆顶元素与最后一个元素交换,并重新调整堆使其满足堆的性质。同样,由于vector是连续存储的,这个操作也可以更高效地进行。 -
deque的存储空间不是连续的,因此在使用时需要更多的空间,可能会导致空间的浪费。
三、priority_queue的常见操作

#include <iostream>
#include <queue>
using namespace std;// priority_queue的常见操作int main()
{// 默认是大堆priority_queue<int> pq;pq.push(3);pq.push(5);pq.push(1);pq.push(4);pq.push(0);while (!pq.empty()){cout << pq.top() << ' ';pq.pop();}cout << endl;return 0;
}
【输出结果】

注意:优先级队列也是不支持迭代器遍历的!!!
四、模拟实现priority_queue
4.1 构造函数
namespace wj
{template<class T, class container = vector<T>>class priority_queue{ private:void AdjustDown(int parent){// 建大堆// 找左右孩子大的size_t child = parent * 2 + 1;while (child < _con.size()){// 保证右孩子存在if (child + 1 < _con.size() && _con[child + 1] > _con[child]) {++child;}if (_con[child] > _con[parent]){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}public:// 无参默认构造priority_queue(){}// 带区间的构造template<class InputIterator>priority_queue(InputIterator first, InputIterator last){// 插入数据while (first != last){_con.push_back(*first);++first;}// 建堆操作 (默认是大堆) for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--){AdjustDown(i);}}private:container _con;};
}
插入一个数据,由于还要保持大堆的性质,如果尾插的结点要比其父结点大,就要进行 向上调整
参考博客:点击跳转
4.2 删除堆顶元素pop
void pop()
{// 头尾结点交换,删除swap(_con[0], _con[_con.size() - 1]);_con.pop_back();// 然后再建堆AdjustDown(0);
}
4.3 插入push
void push(const T& val)
{// 尾部插入一个_con.push_back(val);// 再建堆AdjustUp(_con.size() - 1);
}
4.4 获取堆顶元素top
const T& top()
{return _con[0];
}
4.5 判断是否为空empty
bool empty()
{return _con.empty();
}
4.6 获取个数大小size
size_t size()
{return _con.size();
}
五、仿函数
5.1 什么是仿函数
-
仿函数(函数对象)是一种能够被像函数一样调用的对象。它通常是一个类或者结构体,重载了函数调用运算符
operator(),通过重载这个运算符,我们可以将对象当作函数来使用,实现了类似函数的行为。 -
仿函数可以有自己的状态和成员变量,因此可以在多次调用中保持状态。它可以接受参数,并返回一个值。
例如,定义一个加法仿函数可以这样实现:
struct Add
{int operator()(const int a, const int b) const {return a + b;}
};int main()
{Add add;int result = add(3, 5); // 调用仿函数cout << "add(3, 5) = " << result << endl;return 0;
}
在上面的例子中,Add是一个仿函数,它重载了函数调用运算符 operator(),使得add对象可以像函数一样被调用。通过add(3, 5),我们可以得到结果8
- 在C++ 中,仿函数是一种灵活且强大的编程工具,常常用于算法和标准库中的函数对象。

就比如说sort函数,默认排的是升序
#include <iostream>
#include <algorithm>
using namespace std;int main()
{int arr[] = { 5,1,4,2,0,3 };int arrSize = sizeof(arr) / sizeof(arr[0]);// less<int> 默认可以不写sort(arr, arr + arrSize, less<int>());for (int i = 0; i < arrSize; i++){cout << arr[i] << ' ';}cout << endl;return 0;
}
【输出结果】

less是库里提供的,其作用就是用于小于不等式比较的函数对象类

那么如果想排降序,可以将less替换成greater,这也是库里提供的,其作用是用于大于不等式比较的函数对象类

#include <iostream>
#include <algorithm>
using namespace std;int main()
{int arr[] = { 5,1,4,2,0,3 };int arrSize = sizeof(arr) / sizeof(arr[0]);// less<int> 默认可以不写sort(arr, arr + arrSize, greater<int>());for (int i = 0; i < arrSize; i++){cout << arr[i] << ' ';}cout << endl;return 0;
}
【输出结果】

5.2 实现priority_queue的仿函数
namespace wj
{template<class T, class container = vector<T>, class Compare = less<T>>class priority_queue{private:void AdjustDown(int parent){Compare com;// 建大堆// 找左右孩子大的size_t child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && com(_con[child], _con[child + 1])) // 保证右孩子存在{++child;}if (com(_con[parent], _con[child])){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}void AdjustUp(int child){Compare com;int parent = (child - 1) / 2;while (child > 0){if (com(_con[parent], _con[child])){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}public:// 无参默认构造priority_queue(){}// 带区间的构造template<class InputIterator>priority_queue(InputIterator first, InputIterator last){// 插入数据while (first != last){_con.push_back(*first);++first;}// 建堆操作 (默认是大堆) for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--){AdjustDown(i);}}void pop(){// 头尾结点交换,删除swap(_con[0], _con[_con.size() - 1]);_con.pop_back();// 然后再建堆AdjustDown(0);}void push(const T& val){// 尾部插入一个_con.push_back(val);// 再建堆AdjustUp(_con.size() - 1);}const T& top(){return _con[0];}bool empty(){return _con.empty();}size_t size(){return _con.size();}private:container _con;};
}
注意:如果在priority_queue中放自定义类型的数据,用户需要在自定义类型中提供>或者<的重载。
namespace wj
{template<class T, class container = vector<T>, class Compare = less<T>>class priority_queue{private:void AdjustDown(int parent){Compare com;// 建大堆// 找左右孩子大的size_t child = parent * 2 + 1;while (child < _con.size()){// 改动if (child + 1 < _con.size() && com(_con[child], _con[child + 1])) // 保证右孩子存在{++child;}if (com(_con[parent], _con[child])){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}void AdjustUp(int child){Compare com;int parent = (child - 1) / 2;while (child > 0){if (com(_con[parent], _con[child])){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}public:// 无参默认构造priority_queue(){}// 带区间的构造template<class InputIterator>priority_queue(InputIterator first, InputIterator last){// 插入数据while (first != last){_con.push_back(*first);++first;}// 建堆操作 (默认是大堆) for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--){AdjustDown(i);}}void pop(){// 头尾结点交换,删除swap(_con[0], _con[_con.size() - 1]);_con.pop_back();// 然后再建堆AdjustDown(0);}void push(const T& val){// 尾部插入一个_con.push_back(val);// 再建堆AdjustUp(_con.size() - 1);}const T& top(){return _con[0];}bool empty(){return _con.empty();}size_t size(){return _con.size();}private:container _con;};// 日期类class Date{public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}bool operator<(const Date& d)const{return (_year < d._year) ||(_year == d._year && _month < d._month) ||(_year == d._year && _month == d._month && _day < d._day);}bool operator>(const Date& d)const{return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}friend ostream& operator<<(ostream& _cout, const Date& d);private:int _year;int _month;int _day;};ostream& operator<<(ostream& _cout, const Date& d){_cout << d._year << "-" << d._month << "-" << d._day;return _cout;}
}
相关文章:
【C++初阶】模拟实现优先级队列priority_queue
👦个人主页:Weraphael ✍🏻作者简介:目前学习C和算法 ✈️专栏:C航路 🐋 希望大家多多支持,咱一起进步!😁 如果文章对你有帮助的话 欢迎 评论💬 点赞…...
如何为你的公司选择正确的AIGC解决方案?
如何为你的公司选择正确的AIGC解决方案? 摘要引言词汇解释(详细版本)详细介绍1. 确定需求2. 考虑技术能力3. 评估可行性4. 比较不同供应商 代码快及其注释注意事项知识总结 博主 默语带您 Go to New World. ✍ 个人主页—— 默语 的博客&…...
Windows下将nginx等可执行文件添加为服务
Windows下将nginx等可执行文件添加为服务 为什么将可执行文件添加为服务?将可执行文件添加为服务的步骤步骤 1:下载和安装 Nginx步骤 2:添加为服务方法一:使用 Windows 自带的 sc 命令方法二:使用 NSSM(Non…...
视觉SLAM14讲笔记-第4讲-李群与李代数
李代数的引出: 在优化问题中去解一个旋转矩阵,可能会有一些阻碍,因为它对加法导数不是很友好(旋转矩阵加上一个微小偏移量可能就不是一个旋转矩阵),因为旋转矩阵本身还有一些约束条件,那样再求…...
浅析Redis(1)
一.Redis的含义 Redis可以用来作数据库,缓存,流引擎,消息队列。redis只有在分布式系统中才能充分的发挥作用,如果是单机程序,直接通过变量来存储数据是更优的选择。那我们知道进程之间是有隔离性的,那么re…...
【每日一题】2337. 移动片段得到字符串
【每日一题】2337. 移动片段得到字符串 2337. 移动片段得到字符串题目描述解题思路 2337. 移动片段得到字符串 题目描述 给你两个字符串 start 和 target ,长度均为 n 。每个字符串 仅 由字符 ‘L’、‘R’ 和 ‘_’ 组成,其中: 字符 ‘L’…...
MySQL 数据库常用命令大全(详细)
文章目录 1. MySQL命令2. MySQL基础命令3. MySQL命令简介4. MySQL常用命令4.1 MySQL准备篇4.1.1 启动和停止MySQL服务4.1.2 修改MySQL账户密码4.1.3 MySQL的登陆和退出4.1.4 查看MySQL版本 4.2 DDL篇(数据定义)4.2.1 查询数据库4.2.2 创建数据库4.2.3 使…...
中国移动加大布局长三角,打造算力产业新高地
8月27日,以“数实融合算启未来”为主题的2023长三角算力发展大会在苏州举办,大会启动了长三角算力调度枢纽,携手各界推动算力产业高质量发展。 会上,移动云作为第一批算力资源提供方,与苏州市公共算力服务平台签订算力…...
话费、加油卡、视频会员等充值接口如何对接?
现在很多商家企业等发现与用户保持粘性是越来越难了,大多数的用户活跃度都很差,到底该怎么做才能改善这种情况呢? 那么我们需要做的就是投其所好,在与用户保持粘性的app或者积分商城中投入大家感兴趣的物品或者虚拟产品ÿ…...
服务器重启MongoDB无法启动
文章目录 服务器重启MongoDB无法启动背景规划实施 总结 服务器重启MongoDB无法启动 背景 数据库服务器的CPU接近告警值了,需要添加CPU资源,于是乎就在恰当的时间对服务器进行关机,待添加完资源后开机,这样就完成了CPU资源的添加…...
深度刨析数据在内存中的存储
✨博客主页:小钱编程成长记 🎈博客专栏:进阶C语言 深度刨析数据在内存中的存储 1.数据类型介绍1.1 类型的基本归类 2.整形在内存中的存储2.1 原码、反码、补码2.2 大小端介绍 3.浮点型在内存中的存储3.1 一个例子3.2 浮点数的存储规则3.3指数…...
理解FPGA中的亚稳态
一、前言 大家应该经常能听说到亚稳态这个词,亚稳态主要是指触发器的输出在一段时间内不能达到一个确定的状态,过了这段时间触发器的输出随机选择输出0/1,这是我们在设计时需要避免的。本文主要讲述了FPGA中的亚稳态问题,可以帮助…...
Leetcode86. 分隔链表
给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你应当 保留 两个分区中每个节点的初始相对位置。 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台…...
如何处理 Flink 作业中的数据倾斜问题?
分析&回答 什么是数据倾斜? 由于数据分布不均匀,造成数据大量的集中到一点,造成数据热点。 举例:一个 Flink 作业包含 200 个 Task 节点,其中有 199 个节点可以在很短的时间内完成计算。但是有一个节点执行时间…...
cobbler自动化安装CentOS、windows和ubuntu
环境介绍 同时玩cobbler3.3和cobbler2.8.5 cobbler3.3 系统CentOS8.3 VMware虚拟机 桥接到物理网络 IP: 192.168.1.33 cobbler2.8.5 系统CentOS7.9 VMWare虚拟机 桥接到物理网络 IP:192.168.1.33 安装cobbler3.3 yum源修改 cat /etc/yum.repo.d/Cento…...
springcloud3 GateWay章节-Nacos+gateway动态路由负载均衡4
一 工程结构 1.1 工程 1.2 搭建gatewayapi工程 1.pom文件 <dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13</version><scope>test</scope></dependency><!--gateway--&g…...
RESTful API 面试必问
RESTful API是一种基于 HTTP 协议的 API 设计风格,它提供了一组规范和约束,使得客户端(如 Web 应用程序、移动应用等)和服务端之间的通信更加清晰、简洁和易于理解。 RESTful API 的设计原则 使用 HTTP 协议:RESTful …...
软件机器人助力行政审批局优化网约车业务流程,推动审批业务数字化转型
随着社会的进步和发展,行政审批业务逐渐趋向于智能化和自动化。近日,某市行政审批局在市场准入窗口引入博为小帮软件机器人大幅度提升了网约车办理业务的效率,创新了原有的业务模式。 软件机器人以其自动化、智能化的特性,优化了网…...
飞天使-python的字符串转义字符元组字典等
文章目录 基础语法数据类型python的字符串运算符输入和输出 数据结构列表与元组字典与集合 参考文档 基础语法 数据类型 数值型 ,整数 浮点型 布尔型, 真假, 假范围 字符型 类型转换python的字符串 了解转义字符一些基本的运算 \ 比如一行…...
stm32 uart dma方式接收不定长度字符
一般处理: stm32 uart使用dma接收时,会有自己的数据流中断,数据流中断会调用HAL_UART_RxCpltCallback。但是数据流中断只会在HAL_UART_Receive_DMA函数指定的buffer满时才会触发。 接收不定长度字符,需要和uart的UART_IT_IDLE结…...
前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用
中达瑞和自2005年成立以来,一直在光谱成像领域深度钻研和发展,始终致力于研发高性能、高可靠性的光谱成像相机,为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...
小智AI+MCP
什么是小智AI和MCP 如果还不清楚的先看往期文章 手搓小智AI聊天机器人 MCP 深度解析:AI 的USB接口 如何使用小智MCP 1.刷支持mcp的小智固件 2.下载官方MCP的示例代码 Github:https://github.com/78/mcp-calculator 安这个步骤执行 其中MCP_ENDPOI…...
6.计算机网络核心知识点精要手册
计算机网络核心知识点精要手册 1.协议基础篇 网络协议三要素 语法:数据与控制信息的结构或格式,如同语言中的语法规则语义:控制信息的具体含义和响应方式,规定通信双方"说什么"同步:事件执行的顺序与时序…...
AWS vs 阿里云:功能、服务与性能对比指南
在云计算领域,Amazon Web Services (AWS) 和阿里云 (Alibaba Cloud) 是全球领先的提供商,各自在功能范围、服务生态系统、性能表现和适用场景上具有独特优势。基于提供的引用[1]-[5],我将从功能、服务和性能三个方面进行结构化对比分析&#…...
