【C++】深度解析:用 C++ 模拟实现 priority_queue类,探索其底层实现细节(仿函数、容器适配器)
目录
⭐前言
✨堆
✨容器适配器
✨仿函数
⭐priority_queue介绍
⭐priority_queue参数介绍
⭐priority_queue使用
⭐priority_queue实现
✨仿函数实现
✨堆的向上调整和向下调整
✨完整代码
⭐前言
✨堆
堆是一种特殊的树形数据结构,通常以二叉树的形式实现,具有特定的排序特性。堆分为两种类型:最大堆和最小堆。
具体可以参见这篇文章:【数据结构】二叉树-CSDN博客
✨容器适配器
适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总 结),该种模式是将一个类的接口转换成客户希望的另外一个接口。
STL标准库中stack和queue的底层结构:
虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配器,这是因为stack和队列只是对其他容器的接口进行了包装,STL中stack和queue默认使用deque,比如:
✨仿函数
在 C++ 中,仿函数通常指的是一种行为类似于函数的对象,即可以像调用函数那样被调用的对象。这种对象通常包含一些数据成员,并且重载了括号运算符 operator()
,从而允许以函数的方式调用。
看下面这段代码:
class Less
{
public:bool operator()(const int& x, const int& y){return x < y;}
};int main()
{Less lesscom;cout << lesscom(1, 2) << endl;return 0;
}
代码中定义了Less类,重载(),函数中定义了x、y两个参数,当x小于y就返回true,否则返回false。
在main函数中创建了Less类的对象,如果想要调用重载(),常规的调用方法应该是对象名.函数名(参数列表)。但因为重载()函数是可以省略.operator(),所以当我们使用这个仿函数对象的时候,使用的方法就和调用一个函数一样,这就是仿函数的使用。
仿函数的特点
- 可调用性:仿函数通过重载括号运算符
operator()
实现了可调用性,使得我们可以像调用普通函数一样调用仿函数对象。- 状态:仿函数可以拥有自己的数据成员(状态),这意味着每次调用仿函数时,它可以访问这些成员变量,这与普通的函数不同,后者通常不保留状态。
- 多态性:由于仿函数是对象,它们可以被用作多态的一部分,这意味着你可以通过基类指针或引用调用派生类的仿函数对象。
仿函数的用途
- 算法参数化:仿函数可以作为算法的参数,使得算法可以根据传入的不同仿函数表现出不同的行为。
- 事件处理:在 GUI 编程中,可以使用仿函数作为事件处理器,当事件发生时调用相应的仿函数对象。
- 模板编程:在 C++ 模板编程中,仿函数经常被用作模板参数,以实现泛型算法
⭐priority_queue介绍
priority_queue
是 C++ 标准库中的一个容器适配器,它提供了基于最大堆或最小堆的数据结构来实现优先队列的功能。priority_queue
自动维护元素的排序,使得每次插入或删除操作都能保持堆的性质。
priority_queue
底层将vector作为默认容器,默认情况下为大堆。
⭐priority_queue参数介绍
template <class T, class Container = vector<T>,class Compare = less<typename Container::value_type> > class priority_queue;
-
T
: 这是优先队列中存储的元素类型。例如,如果存储整数,则T
将是int
。 -
Container
: 这是一个可选的模板参数,用来指定底层容器的类型。默认情况下,std::priority_queue
使用std::vector<T>
作为其底层容器。但是,可以选择任何支持随机访问迭代器的容器类型,例如std::deque<T>
。请注意,底层容器必须支持push_back
和pop_back
操作。 -
Compare
: 这也是一个可选的模板参数,用来指定元素之间的比较方式。默认情况下,使用std::less<typename Container::value_type>
,这意味着对于类型T
的元素,将使用<
运算符进行比较,创建出一个大堆。如果要创建一个最小堆,则可以使用std::greater<typename Container::value_type>
。
我们其实可以发现 priority_queue
使用的是容器适配器模式,底层是vector和deque这样支持下标随机访问等操作的容器;并且还是要了仿函数 Compare
来控制比较逻辑,使用less<typename Container::value_type>创建出大堆,使用greater<typename Container::value_type> 创建出小堆。
priority_queue的
一个完整的声明如下:priority_queue<int, std::vector<int>, std::greater<int>> minHeap;
⭐priority_queue使用
优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成 堆 的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。
注意: 默认情况下priority_queue是大堆。
函数声明 | 接口说明 |
priority_queue()/priority_queue(first, last) | 构造一个空的优先级队列 |
empty() | 检测优先级队列是否为空,是返回true,否则返回 false |
top() | 返回优先级队列中最大(最小元素),即堆顶元素 |
push(x) | 在优先级队列中插入元素x |
pop() | 删除优先级队列中最大(最小)元素,即堆顶元素 |
默认情况下,priority_queue是大堆。
#include <vector>
#include <queue>
#include <functional> // greater算法的头文件
void TestPriorityQueue()
{// 默认情况下,创建的是大堆,其底层按照小于号比较vector<int> v{3,2,7,6,0,4,1,9,8,5};priority_queue<int> q1;for (auto& e : v)q1.push(e);cout << q1.top() << endl;// 如果要创建小堆,将第三个模板参数换成greater比较方式priority_queue<int, vector<int>, greater<int>> q2(v.begin(), v.end());cout << q2.top() << endl;
}
如果在priority_queue中放自定义类型的数据,用户需要在自定义类型中提供> 或者< 的重载。
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){_cout << d._year << "-" << d._month << "-" << d._day;return _cout;}
private:int _year;int _month;int _day;
};
void TestPriorityQueue()
{// 大堆,需要用户在自定义类型中提供<的重载priority_queue<Date> q1;q1.push(Date(2018, 10, 29));q1.push(Date(2018, 10, 28));q1.push(Date(2018, 10, 30));cout << q1.top() << endl;// 如果要创建小堆,需要用户提供>的重载priority_queue<Date, vector<Date>, greater<Date>> q2;q2.push(Date(2018, 10, 29));q2.push(Date(2018, 10, 28));q2.push(Date(2018, 10, 30));cout << q2.top() << endl;
}
⭐priority_queue实现
✨仿函数实现
仿函数就是它的对象可以想函数一样去使用,本质上是重载了()。
//仿函数//控制大堆
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;}
};
实际上的使用应该像下面图中第二行那样,但是重载之后就省略了。
✨堆的向上调整和向下调整
大体上的逻辑和堆的实现相同,但是使用仿函数控制比较的逻辑,使得优先队列不仅对基础数据类型,如int,有效,也对想Date这样的日期类型有效(需要重载了>和<)。
//向上调整
void adjust_up(size_t 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;}}
}
//向下调整
void adjust_down(size_t 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;}}
}
✨完整代码
//仿函数//控制大堆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;}};//less 控制为大堆template<class T,class Container = vector<T>,class Compare = less<T>>class priority_queue{public://向上调整void adjust_up(size_t 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 push(const T& x){_con.push_back(x);adjust_up(_con.size()-1) ;}//向下调整void adjust_down(size_t parent){Compare com;size_t child = parent * 2 + 1;while (child < _con.size()){//if (child + 1 < _con.size() && _con[child + 1] > _con[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 (_con[parent] < _con[child])if (com(_con[parent], _con[child])){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}bool empty(){return _con.empty();}size_t size(){return _con.size();}const T& top(){return _con[0];}private:Container _con;};
____________________
⭐感谢你的阅读,希望本文能够对你有所帮助。如果你喜欢我的内容,记得点赞关注收藏我的博客,我会继续分享更多的内容。⭐
相关文章:

【C++】深度解析:用 C++ 模拟实现 priority_queue类,探索其底层实现细节(仿函数、容器适配器)
目录 ⭐前言 ✨堆 ✨容器适配器 ✨仿函数 ⭐priority_queue介绍 ⭐priority_queue参数介绍 ⭐priority_queue使用 ⭐priority_queue实现 ✨仿函数实现 ✨堆的向上调整和向下调整 ✨完整代码 ⭐前言 ✨堆 堆是一种特殊的树形数据结构,通常以二叉树的…...

1个人躲,5个人抓!《极限竞速:地平线5》全新游戏模式“捉迷藏”即将推出
风靡全球的赛车竞速游戏《极限竞速:地平线5》即将推出全新游戏模式——捉迷藏(Hide & Seek)。 《极限竞速:地平线5》日前发布了全新预告,展示了即将于 9 月 10 日推出的捉迷藏模式游戏玩法。该预告是日前举办的2024 年科隆国际游戏展 Xb…...

ARCGIS XY坐标excel转要素面
1、准备好excel 坐标 excel文件转为csv才能识别,CSV只能保留第一个工作表并且,不会保留格式。 2、在ArcGis中导入XY事件图层 创建XY事件图层 图层要素赋对象ID 将导入的图层导出为先新的图层,这样就给每个要素附加了唯一的值 选择点集转线…...

MyBatis源码系列3(解析配置文件,创建SqlSessionFactory对象)
创建SqlSessionFactory; 首先读取配置文件,使用构造者模式创建SqlSessionFactory对象。 InputStream inputStream Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder…...

企业级web应用服务器tomcat
目录 一、Web技术 1.1 HTTP协议和B/S 结构 1.2 前端三大核心技术 1.2.1 HTML 1.2.2 CSS(Cascading Style Sheets)层叠样式表 1.2.3 JavaScript 二、tomcat的功能介绍 2.1 安装 tomcat 环境准备 2.1.1 安装java环境 2.1.2 安装并启动tomcat …...

深入浅出,探讨IM(即时通讯-聊天工具)技术架构及用户界面设计
在数字化时代的浪潮中,即时通讯(IM)工具已然成为人们日常沟通的重要方式。从微信、QQ到飞信钉、喧喧IM、企业微信、钉钉、Slack,这些IM工具不仅为我们提供了便捷的沟通方式,更在技术架构和用户界面设计上展现了独特的魅…...

小米、友邦带领恒指大反攻!
港股三大指数反弹止步2连跌,恒生科技指数一度冲高至2%,恒指收涨1.44%。盘面上,大型科技股多数表现活跃,业绩超预期,小米大涨超8%表现尤其抢眼,京东涨约4%,百度涨1.71%,网易涨2.14%&a…...

中国植物性状数据库
中国植物性状的研究主要集中在植物的生理结构和功能,以及它们对环境的适应性上。中国植物性状的多样性体现在多个方面,包括植物的生理结构、生长习性、以及对环境的适应性等。 中国植物性状数据库,包含了来自140个样点的1529种植物…...

[数据集][目标检测]街灯路灯检测数据集VOC+YOLO格式1893张1类别
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):1893 标注数量(xml文件个数):1893 标注数量(txt文件个数):1893 标注…...

C++位运算
C位运算 运算符 & 按位与 如果两个相应的二进制位都为1,则该位的结果值为1,否则为0 | 按位或 两个相应的二进制位中只要有一个为1,该位的结果值为1 ^ 按位异或 若参加运算的两个二进制位值相同则为0,否则为1 ~ 取反 ~是一元…...

Day97:云上攻防-云原生篇KubernetesK8s安全APIKubelet未授权访问容器执行
知识点: 1、云原生-K8s安全-名词架构&各攻击点 2、云原生-K8s安全-Kubelet未授权访问 3、云原生-K8s安全-API Server未授权访问 K8S集群 Kubernetes是一个开源的,用于编排云平台中多个主机上的容器化的应用,目标是让部署容器化的应用…...

招聘|头部云厂商招 PG 核心骨干 DBA【上海】
我们的招聘专区又回来了!🏃 Bytebase 作为先进的数据库 DevOps 团队协同工具 🔧,用户群里汇聚了 💗 业界优秀的 DBA,SRE,运维的同学们 🌟。 上周用户群里有小伙伴发招聘信息 &…...

继承(下)【C++】
文章目录 子类继承父类之后,子类的默认成员函数的变化构造函数编译器自动生成的构造函数程序员手动写的构造函数 拷贝构造编译器自动生成的拷贝构造函数程序员手动写的拷贝构造函数 赋值重载编译器自动生成的赋值重载程序员手动写的赋值重载 析构函数 继承与友元菱形…...

AI模拟器
一、介绍 基于鸿蒙Next模拟一个ai对话过程二、场景需求 客户服务、数据分析、个性化推荐、图像和视频处理、智能家居、交通管理、教育行业、制造等等。 三、业务步骤 第一步:输入框提出问题,发送问题, 第二部:下次发送࿰…...

【C++二分查找 前缀和】1658. 将 x 减到 0 的最小操作数
本文涉及的基础知识点 C二分查找 C算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 LeetCode1658. 将 x 减到 0 的最小操作数 给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素&am…...

验证实战知识点--(2)
1.seq中的pre_start pre_start 是 uvm_sequence 类的一个虚拟方法,用于在序列开始执行之前进行初始化和设置。这个方法在调用 start 方法前立即执行,提供了一个执行自定义初始化代码的机会。 start 方法用于启动序列的执行,而 pre_start 可以…...

【图文并茂】ant design pro 如何优雅地把删除和批量删除功能合并到一起,并抽出来成为组件
如上图所示,其实批量删除和删除应该算是一个功能 只是删除一个或多个的区别 那么接口应该用的同一个 删除一个的时候呢,就传 一个对象_id 过去 删除多个的时候,就传 多个 对象 _id 的数组过去 后端 const deleteMultipleRoles handleAs…...

监控篇之利用dcgm-exporter监控GPU指标并集成grafana大盘
一、应用场景 当环境中包含GPU节点时,需要了解GPU应用使用节点GPU资源的情况,例如GPU利用率、显存使用量、GPU运行的温度、GPU的功率等。 在获取GPU监控指标后,用户可根据应用的GPU指标配置弹性伸缩策略,或者根据GPU指标设置告警…...

获取当前路由器的外网IP(WAN IP)
GPT-4o (OpenAI) 获取当前路由器的外网IP(WAN IP)可以通过以下几种方法: 1. 访问路由器管理页面: - 通常路由器的管理页面可以通过在浏览器中输入路由器的IP地址来访问(例如,192.168.0.1 或 192.168.1…...

QT Creator UI中文输入跳出英文
笔者用的是QQ拼音输入,发现只要在UI中加入了QTableWidget,输入多几次中文,就会跳入英文。 后面改用搜狗拼音稍微好一些,但是偶尔还是插入了空格。...

Java基础核心知识学习笔记
方法重载 请记住下面重载的条件 方法名称必须相同。参数列表必须不同(个数不同、或类型不同、参数类型排列顺序不同等)。方法的返回类型可以相同也可以不相同。仅仅返回类型不同不足以成为方法的重载。重载是发生在编译时的,因为编译器可以根…...

Leetcode 237.19.83.82 删除链表重复结点 C++实现
Leetcode 237. 删除链表中的节点 问题:有一个单链表的head,我们想删除它其中的一个节点node。给你一个需要删除的节点 node 。你将 无法访问 第一个节点head。链表的所有值都是唯一的,并且保证给定的节点 node不是链表中的最后一个节点。删除…...

Spring OAuth2.0资源服务源码解析
主要分析spring-security-oauth2-resource-server的源码,介绍OAuth2.0授权码模式下Spring Boot OAuth2资源服务的运行流程,分析其是如何对令牌进行认证的,并展示资源服务配置 代码版本信息 Spring Boot 2.7.10 spring-security-oauth2-resou…...

JavaScript 原型与原型链
原型与原型链 要讨论原型与原型链,就要先了解什么是 构造函数 ,构造函数与普通函数没有太大的区别,使用 new关键字 创建实例对象的函数,就叫做构造函数。 在js中,每一个函数类型的数据都有一个 .prototype 的属性&am…...

Spring Boot实现简单的Oracle数据库操作
使用到的技术: 1. Spring Boot:用于简化Spring应用的开发。 2. Dynamic DataSource:实现动态多数据源的访问和切换 3. Oracle JDBC Driver:与Oracle数据库进行连接和交互。 4. Mybatis-Plus:简化SQL映射和数据库访问。…...

微软发布 Phi-3.5 系列模型,涵盖端侧、多模态、MOE;字节 Seed-ASR:自动识别多语言丨 RTE 开发者日报
开发者朋友们大家好: 这里是 「RTE 开发者日报」 ,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE(Real-Time Engagement) 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文…...

笔记:Echarts柱状图 实现滚轮条 数据太多
效果👇👇👇 配置:👇 let option {dataZoom: [{type: "slider",show: true,zoomLock: true,start: 0,end: 20,bottom: 60,height: 10,textStyle: {color: "transparent",fontSize: 9,},fillerColo…...

嵌入式学习day17(数据结构)
大纲 数据结构、算法数据结构: 1. 线性表:顺序表、链表(单向链表,单向循环链表,双向链表,双向循环链表)、栈(顺序栈,链式栈)、队列(循…...

网站怎么做敏感词过滤,敏感词过滤的思路和实践
敏感词过滤是一种在网站、应用程序或平台中实现内容审查的技术,用于阻止用户发布包含不适当、非法或不符合政策的内容。我们在实际的网站运营过程中,往往需要担心某些用户发布的内容中包含敏感词汇,这些词汇往往会导致我们的网站被用户举报&a…...

【峟思】如何使用投入式水位计才能确保测量准确性
在水利、环保、工业监测等众多领域,水位测量是一项至关重要的任务,它不仅直接关系到水资源的合理利用与保护,还影响到防洪、供水、排水等多个方面的安全与效率。投入式水位计作为一种常见的水位测量工具,以其结构简单、测量准确、…...