【C++笔记】优先级队列priority_queue的模拟实现

【C++笔记】优先级队列priority_queue的模拟实现

  • 一、优先级队列的介绍与使用方式
    • 1.1、优先级队列介绍
    • 1.2、优先级队列的常见使用
  • 二、优先级队列的模拟实现
    • 1.0、仿函数的介绍
    • 1.1、构造函数
    • 1.2、优先级队列的插入push
    • 1.3、优先级队列的删除(删除堆顶元素)
    • 1.4、获取堆顶元素
    • 1.5、判断是否为空
    • 1.6、优先级队列的大小size

一、优先级队列的介绍与使用方式

1.1、优先级队列介绍

优先级队列可能听名字就能想到它的功能,就是按优先级排的队列。可他到底是个什么呢?它的底层有时由什么实现的?
我们可以先翻翻文档看看:
在这里插入图片描述
从文档中我们也可以看出它其实也是一个类模板。
其中的Container这个模板参数是一个容器适配器,默认使用vector作为其底层存储数据的容器。
其实优先级队列在底层就是我们以前学过的堆,它在vector上使用了堆heap的算法将vector中元素构造堆的结构,默认情况下是大堆。

而其中的Compare这个模板参数则是能将堆灵活的转换成大堆或小堆的一个非常好的工具,在后面会对仿函数进行详细的解析。

我们再来看看它对应的接口:
在这里插入图片描述
从给出的接口我们也可以发现它和堆其实是非常相似的。

1.2、优先级队列的常见使用

上面说过优先级队列默认是大堆,那我们就来验证一下:
在这里插入图片描述
如果想要给成小堆,则要传入一个仿函数:
在这里插入图片描述
因为是缺省参数,所以一定要从左到右传完。这里的greater< int > 其实就是比较方法。

二、优先级队列的模拟实现

1.0、仿函数的介绍

仿函数,也称函数对象,是STL六大组件中的一个,他可以想其他类一样定义对象,也可以像正常函数一样调用。它其实有点像我们在C语言中学过的函数指针。

但是我们在这里并不能一次性讲解完它的所有内容,所以今天就仅基于priority_queue的实现来对它进程粗略的介绍。

实际上,就实现意义而言,函数对象这个名字更加贴切:一种具有函数特质的对象。但是,仿函数似乎能更加符合的描述他的行为。所以这里我们就采用仿函数这种叫法。

在学习STL之前我们就已经了解了泛型编程的概念,C++引入了模板让我们的编程能够随意的控制数据类型,现在引入了仿函数的概念,让我们能够控制逻辑。

我们上面用到的greater< int>就是一个仿函数,我们也可以对应的查查文档看看:
在这里插入图片描述

我们再来看看它的简单应用吧:

模拟实现仿函数:

template<class T>
class Less
{
public:bool operator()(const T& x, const T& y)//less和greater需要实现的就是一个比较,所以这里的返回值是bool类型{return x < y;}
};
template<class T>
class Greater
{
public:bool operator()(const T& x, const T& y){return x > y;}
};

其实仿函数主要是通过在类中实现一个特殊的运算符重载——operator()来达成的。
然后我们就可以将上面的测试换成我们自己写的仿函数了:
在这里插入图片描述
其达到的效果也是一样的。
其实仿函数之所以能像函数一样直接调用(直接对象名+括号),其实是编译器在底层做了处理,编译器在底层其实是自动调用了类中的operator(),而我们其实也可以显示的写出来:
在这里插入图片描述
从这点我们可以看出,仿函数其实也没有多神奇,一切都是编译器的功劳。

1.1、构造函数

模板参数的设计:
首先,对于函数模板的设计,我们和库里面对其,给了三个参数,分别表示参数存入容器的参数类型,容器类型和仿函数,其中默认的仿函数是less,建大堆:

template<class T, class Container = std::vector<T>, class Compare = less<T>>
class Priority_queue
{
public://...
private:Container _con;Compare _cmp;}; 

无参构造函数:
无参构造函数我们其实可以不写的,因为我们使用容器适配器的好处就是我们可以直接使用其他容器的无参构造函数,但是因为后面的一些原因(调用可能存在歧义),我们还得将它写上。
其实我们只需将它写出来即可,我们什么都不用做:

	// 无参构造函数Priority_queue() {}

迭代器区间构造:
因为我们这里使用的是vector来适配,vector中也有迭代器区间构造,所以我们也很轻松,直接调用vector的即可:

	// 迭代器区间构造函数template <class InputIterator>Priority_queue(InputIterator first, InputIterator last):_con(first, last){}

1.2、优先级队列的插入push

既然有限就队列的底层是一个堆,那它的插入也和堆一样,建堆使用的是向上调整算法,我们先来简单的回顾一下:
以小堆为例:
在这里插入图片描述

建小堆我们的向上调整算法需要做的就是,从指定的孩子节点位置开始,和父节点比较,判断是否满足堆结构,如果不满足,就交换父子节点,然后原来的父节点编程子节点,再次进行上述操作,直到满足堆结构为止。

以下是代码实现:

// 向上调整算法
void adjust_up(size_t child) {size_t parent = (child - 1) / 2;while (child > 0) {if (_cmp(_con[parent], _con[child])) {swap(_con[parent], _con[child]);}else {break;}child = parent;parent = (child - 1) / 2;}
}

然后我们的插入接口就像堆一样,每插入一个节点就向上调整一次即可:

// 插入
void push(const T& x) {_con.push_back(x);adjust_up(_con.size() - 1);
}

1.3、优先级队列的删除(删除堆顶元素)

删除pop使用的是向下调整算法,我们还是先来简单的回顾一下:
还是以小堆为例:
在这里插入图片描述
如上图,向下调整算法我们要做的是找到两个孩子中较小的,然后与父节点比较大小,如果父节点大于子节点就执行交换,然后原来的子节点成为新的父节点,再次进行上述步骤。直到符合堆的结构。

这是代码实现:

// 向下调整算法
void adjust_down(size_t parent) {size_t child = 2 * parent + 1;while (child < _con.size()) {if (child + 1 < _con.size() && _cmp(_con[child], _con[child + 1])) {child++;}if (_cmp(_con[parent], _con[child])) {swap(_con[parent], _con[child]);}parent = child;child = 2 * parent + 1;}
}

我们堆中的删除是使用了一个巧妙的方式来节省开销,先将顶部的元素与最后一个元素位置互换,然后再删除最后一个元素,再堆顶部元素进行向下调整:

	// 弹出void pop() {swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}

1.4、获取堆顶元素

其实堆最主要的接口就上面这两个,实现了上面的两个接口,其他的接口就很简单了,简直不用动脑。

获取顶部元素,我们直接返回_con.front()即可:

	 // 顶部元素T& top() {return _con.front();}

1.5、判断是否为空

因为vector本身就又判空接口,所以没的说,也是复用:

// 判空
bool empty() {return _con.empty();
}

1.6、优先级队列的大小size

这个也一样:

// 长度
size_t size() {return _con.size();
}

测试结果:
小堆:
在这里插入图片描述
大堆:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/138757.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自然语言处理(一):RNN

「循环神经网络」&#xff08;Recurrent Neural Network&#xff0c;RNN&#xff09;是一个非常经典的面向序列的模型&#xff0c;可以对自然语言句子或是其他时序信号进行建模。进一步讲&#xff0c;它只有一个物理RNN单元&#xff0c;但是这个RNN单元可以按照时间步骤进行展开…

classification_report分类报告的含义

classification_report分类报告 基础知识混淆矩阵&#xff08;Confusion Matrix&#xff09;TP、TN、FP、FN精度&#xff08;Precision&#xff09;准确率&#xff08;Accuracy&#xff09;召回率&#xff08;Recall&#xff09;F1分数&#xff08;F1-score&#xff09; classi…

管理员模式运行cmd或则bat文件的时候,出现路径错误的问题

最近在使用Comfyui, 不清楚啥原因&#xff0c;有时候Git无法访问&#xff0c;有时候文件夹无法访问的。就想把它的运行bat命令直接用 管理员模式运行&#xff0c;给到最高的权限&#xff0c;试试。但就这么简单的问题&#xff0c;搜了半天&#xff0c;都是一大堆不靠谱的教程&a…

大数据毕业设计选题推荐-污水处理大数据平台-Hadoop-Spark-Hive

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

selenium+python做web端自动化测试框架实战

最近受到万点暴击&#xff0c;由于公司业务出现问题&#xff0c;工作任务没那么繁重&#xff0c;有时间摸索seleniumpython自动化测试&#xff0c;结合网上查到的资料自己编写出适合web自动化测试的框架&#xff0c;由于本人也是刚刚开始学习python&#xff0c;这套自动化框架目…

数字马力笔试面试复盘

笔试——10月9日19&#xff1a;00 单选&#xff1a;30题 16.如何获取AJAX 请求的响应状态码? A通过AJAX对象的 statusCode 属性获取 B通过AJAX对象的responseText 属性获取C通过AJAX对象的status 属性获取 D通过AJAX对象的responseCode属性获取 答案&#xff1a;可以通过AJAX…

osg点云加载与渲染

目录 效果 laslib 关键代码 完整代码 效果 las点云读取使用了laslib这个库。 laslib 关键代码 {// 这里演示读取一个 .txt 点云文件const char* lasfile path.c_str();std::ifstream ifs;ifs.open(lasfile, std::ios::in | std::ios::binary);liblas::ReaderFactory f;libl…

Spring Cloud学习(三)【Nacos注册中心】

文章目录 认识 NacosNacos 安装使用 Nacos 完成服务注册Nacos 服务分级存储模型集群负载均衡策略 NacosRule根据权重负载均衡Nacos 环境隔离&#xff08;命名空间&#xff09;Nacos 和 Eureka 的区别 认识 Nacos Nacos 是阿里巴巴的产品&#xff0c;现在是 SpringCloud 中的一…

Azure - 机器学习:自动化机器学习中计算机视觉任务的超参数

Azure Machine Learning借助对计算机视觉任务的支持&#xff0c;可以控制模型算法和扫描超参数。 这些模型算法和超参数将作为参数空间传入以进行扫描。 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&#xff0c;同济…

【C++入门】构造函数析构函数

目录 前言 1. 类的默认成员函数 2. 构造函数 2.1 什么是构造函数 2.2 构造函数的特性 3. 析构函数 3.1 什么是析构函数 3.2 析构函数的特性 前言 前边我们已经了解了类和对像的基本概念&#xff0c;今天我们将继续深入了解类。类有6个默认成员函数&#xff0c;即使类中什么都…

C#开发的OpenRA游戏之世界存在的属性CombatDebugOverlay(3)

C#开发的OpenRA游戏之世界存在的属性CombatDebugOverlay(3) 这次来分析CombatDebugOverlay属性,这个属性只有在调试游戏的时候才会使用。当你设置这个属性的时候,就可以看到如下图的结果: 可以看到物品的周边都有一个圆圈,以及有一些十字的点位标志。 那些十字表示的点…

Python实现局部二进制算法(LBP)

1.介绍 局部二进制算法是一种用于获取图像纹理的算法。这算法可以应用于人脸识别、纹理分类、工业检测、遥感图像分析、动态纹理识别等领域。 2.示例 """ 局部二进制算法&#xff0c;计算图像纹理特征 """ import cv2 import numpy as np imp…

小程序游戏对接广告收益微信小游戏抖音游戏软件

小程序游戏对接广告是一种常见的游戏开发模式&#xff0c;开发者可以通过在游戏中嵌入广告来获取收益。以下是一些与小程序游戏对接广告收益相关的关键信息&#xff1a; 小程序游戏广告平台选择&#xff1a; 选择适合你的小程序游戏的广告平台非常重要。不同的平台提供不同类型…

iview实现table里面每行数据的跳转

我的需求是跳转到第三方网站&#xff0c;看官方是写了如何跳转站内路由&#xff0c;不符合我的要求&#xff0c;在csdn发现了一篇文章&#xff0c;我贴一下代码 <template><Table border :columns"ReportColumns" :data"ReportData"><templ…

odoo16 库存初始化 excel导入问题

最近在为一家公司实施odoo时&#xff0c;发现库存模块实施过程中按用户实际&#xff0c;产品初始化就是个问题。下面一一记录下 一个新公司&#xff0c;产品都有上百种&#xff0c;甚致几千种&#xff0c;如何把现有产品数据录入系统就是个不小的活。odoo16是有导入导出功能不…

悟空crm二次开发 增加客户保护功能 (很久没有消息,但是有觉得有机会的客户)就进入了保护转态

需求&#xff1a;客户信息录入不限数量&#xff0c;但是录入的信息1个月内只有自己和部门领导能看到&#xff0c;如果1个月内未成交或者未转移至自己的客保 则掉入公海所有人可见&#xff0c;这里所说的客保就是现在系统自带的客保 1、需求思维导图 2、新增保护按钮 3、点击该…

C++初阶--类与对象(3)(图解)

文章目录 再谈构造函数初始化列表隐式类型转换explicit关键字 static成员友元类内部类匿名对象拷贝函数时的一些优化 再谈构造函数 在我们之前的构造函数中&#xff0c;编译器会通过构造函数&#xff0c;对对象中各个成员给出一个适合的初始值&#xff0c;但这并不能称之为初始…

QT 布局管理综合实例

通过一个实例基本布局管理&#xff0c;演示QHBoxLayout类、QVBoxLayout类及QGridLayout类效果 本实例共用到四个布局管理器&#xff0c;分别是 LeftLayout、RightLayout、BottomLayout和MainLayout。 在源文件“dialog.cpp”具体代码如下&#xff1a; 运行效果&#xff1a; Se…

Redis的内存淘汰策略分析

概念 LRU 是按访问时间排序&#xff0c;发生淘汰的时候&#xff0c;把访问时间最久的淘汰掉。LFU 是按频次排序&#xff0c;一个数据被访问过&#xff0c;把它的频次 1&#xff0c;发生淘汰的时候&#xff0c;把频次低的淘汰掉。 几种LRU策略 以下集中LRU测率网上有很多&am…

浙江大学漏洞报送证书

获取来源&#xff1a;edusrc&#xff08;教育漏洞报告平台&#xff09; url&#xff1a;主页 | 教育漏洞报告平台 兑换价格&#xff1a;20金币 获取条件&#xff1a;提交浙江大学任意中危或以上级别漏洞