priority_queue 的模拟实现

priority_queue 的底层结构

我们已经学习过栈和队列了,他们都是用一种容器适配出来的。今天我们要学习的 prority_queue 也是一个容器适配器。在 priority_queue 的使用部分我们已经知道想要适配出 priority_queue,这个底层的容器必须有以下接口:

  • empty():检测容器是否为空。
  • size():返回容器中有效元素个数。
  • front():返回容器中第一个元素的引用。
  • push_back():在容器尾部插入元素。

适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口。

在学习 C 语言的时候,我们已经学习过堆这种数据结构了,当时我们使用的是用数组作为堆的底层数据结构,因此,我们模拟实现 priority_queue 会使用 vector 作为 priority_queue 的底层数据结构。当然 deque 也是没有问题的,但是不及 vector 高效。因为我们会大量使用 operator[] ,对比下来我们会倾向于选择 vector 来模拟实现 priority_queue

priority_queue 的基本结构实现

实现 priority_queue 的大体思路和模拟实现 stackqueue 差不多。priority_queue 涉及更多的算法和细节处理。

#pragma once
namespace Tchey
{template<class T, class Container = vector<T>>class priority_queue{public:private:Container _con;};
}

但是我们这样定义出来的 priority_queue 的模板类好像不对啊!因为库里面的 priority_queue 想要构建小堆的时候,需要传递三个模板参数哒:存储 int 类型的小堆:priority_queue<int, vector<int>, greater<int>> heap。这个greater<int> 是个什么东西呢?

这个 greater<int> 就是一个模板类 priority_queue 能够实例化出来大堆和小堆的关键所在!

仿函数

看他的结构,greater<int> 应该也是一个模板类。在这个类的里面,重载了圆括号运算符,即:operator(),使得一个类的对象,能够像函数那样调用。这个东东就叫做仿函数。我们来看一个简单的例子吧:

struct GetLessNum
{int operator()(const int& a, const int& b){return a < b ? a : b; }
};int main()
{GetLessNum getLessNum;int a = 10, b = 5;cout << getLessNum(a, b) << endl; //输出:5return 0;
}

在上面的例子中,我们定义了一个 GetLessNum 的类,类中重载了圆括号运算符。在 main 函数中,实例化出了一个对象,通过对象调用 operator() 从而通过类对象实现了函数调用的效果,这个就是仿函数啦!

揭秘 greater<int>

同样地,在 greater 这个模板类中,也重载了圆括号运算符。当我们构建大堆的时候,没有传入后两个模板参数,可见是给了缺省值。在 C 语言阶段,我们已经学习了堆,知道了构建大堆与小堆的区别:仅仅是在向上调整算法,和向下调整算法中的比较逻辑上不同,其余均是相同的。

C语言数据结构初阶(11)----堆_姬如祎的博客-CSDN博客

于是我们就可用通过模板参数来控制 priority_queue 中两个算法实现的比较逻辑!这样就能够实现根据传入模板参数的不同,构建出来不同的堆了!

namespace Tchey
{template<class T>struct less{bool operator()(const T& e1, const T& e2){return e1 < e2;}};template<class T>struct greater{bool operator()(const T& e1, const T& e2){return e1 > e2;}};
}

在使用 priority_queuepush 函数的时候,就会使用向上调整算法:对于大堆,如果子节点的值大于父节点的值,那么就需要交换两个节点的值,对于小堆;如果子节点的值小于父节点的值,那么就需要交换两个节点的值。

于是我们就可以通过传入的第三个模板参数来控制:如果传入 less<T>,刚好 less<T> 中的 opertor() 是小于的比较逻辑,就是小堆的向上调整算法;如果传入 greater<T>,刚好 greater<T> 中的 opertor() 是大于的比较逻辑,就是大堆的向上调整算法。

通过模板参数的控制,priority_queue 同时能够实现大堆和小堆,而不用单独写大堆和小堆。

priority_queue 的基本结构就可以这么写:

namespace Tchey
{template<class T>struct less{bool operator()(const T& e1, const T& e2){return e1 < e2;}};template<class T>struct greater{bool operator()(const T& e1, const T& e2){return e1 > e2;}};template<class T, class Container = vector<T>, class Cmp = Less<T>>class priority_queue{public:private:Container _con;};
}

priority_queue 的函数实现

向上调整算法

我们在向一个堆插入数据的时候就会用到向上调整算法:就拿大堆来说,你向大堆里面插入了一个数据,自然是要通过调整,使得插入的数据和原来的大堆重新形成一个新的大堆!

在这里插入图片描述

在这个插入的例子中,插入节点 9,不妨命名为 child。他的父节点 6,不妨命名为 parent。child 大于parent,交换两个节点的值。然后更新 child 和 parent,此时 child 依然大于 parent 交换两个节点的值。再次更新 child 和 parent。发现 parent < 0,结束循环。或者在比较的过程中 child < parent 也要结束循环。

这就是大堆的向上调整算法,至于小堆,比较逻辑反过来就行。

想要 priority_queue 根据传入的参数来决定是大堆的比较逻辑还是小堆的比较逻辑,这里就不能将比较逻辑写死,而是根据仿函数来比较!

void AdjustUp(int child)
{Cmp cmp; //根据第三个模板参数的类,实例化出来一个对象int parent = (child - 1) / 2; //根据child 找到 parentwhile(child){if(cmp(_con[parent], _con[child])) //调用 operator() 来比较{swap(_con[parent], _con[child]);child = parent;parent = (child - 1) / 2;}else //不满足条件直接退出循环break;}
}

向下调整算法

向下调整算法在 pop 函数中使用哈!pop 的逻辑就是将堆顶的数据与堆中最后一个数据交换,然后对下标为 0 的元素来一次向下调整算法,就满足堆的要求啦!

在这里插入图片描述

我们来看上面的例子:这是一个大堆,删除堆顶的元素:我们先将堆顶的 7 和堆的最后一个数据 0 交换。然后对下标为 0 的元素,不妨命名为 parent。向下调整的逻辑是:选择 parent 的左右孩子中较大的那个孩子,不妨命名为 child,然后与 parent 进行比较,如果 child 大于 parent,那么交换 parent 和 child 两个节点。反之结束向下调整算法的逻辑。如果当 child 大于等于 vector 的 size 也要结束循环。

同向上调整算法,如果是小堆的话,只是比较逻辑不相同,其他的步骤均是一样的!因此向下调整算法 child 与 parent 的比较不能使用大于小于符号将比较逻辑写死。而是要使用仿函数来实现比较逻辑。

void AdjustDown(int parent)
{Cmp cmp; //实例化仿函数的对象int n = _con.size(); //vector 的大小int child = parent * 2; //通过 parent 找到 childwhile(child < n){//选择左右孩子中较大的那个或者较小的那个,取决于第三个模板参数的传值if(child + 1 < n && cmp(_con[child], _con[child + 1]))child++;//如果满足条件,交换if(cmp(_con[parent], _con[child])){swap(_con[parent], _con[child]);parent = child;child = parent * 2;}else //不满足直接退出break;}
}

void push(const T& val)

当你完成了向上调整算法与向下调整算法的书写,堆的接口实现就是信手拈来哈!push 函数是向堆中插入如一个元素,我们只需要在 vectorpush_back 一个元素,然后使用向上调整算法就可以啦!

void push(const T& val)
{_con.push_back(val);AdjustUp(_con.size() - 1);
}

void pop()

删除堆顶的元素只需要我们将 vector 中下标为 0 的元素与 vector 中的最后一个元素交换位置,然后调用 pop_back() 接口,最后对下标为 0 的元素使用一次向下调整算法就可以啦!


void pop()
{swap(_con[0], _con[_con.size() - 1]);_con.pop_back();AdjustDown(0);
}

bool empty()

我们只需要返回 vector 是否为空就行啦!

void empty()
{return _con.empty();
}

size_t size()

同样地,我们只需要返回 vector 的 size 就可以啦!


size_t size()
{return _con.size();
}

T& top()

返回堆顶的元素就是返回 vector 中下标为 0 的元素。


void top()
{assert(_con.size());return _con[0];
}

在这里插入图片描述

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

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

相关文章

安装Python环境

Python 安装包下载地址&#xff1a;https://www.python.org/downloads/ 打开该链接&#xff0c;可以看到有两个版本的 Python&#xff0c;分别是 Python 3.x 和 Python 2.x&#xff0c;如下图所示&#xff1a; Python下载页面截图 图 1 Python 下载页面截图&#xff08;包含…

基于单片机设计的防煤气泄漏装置

一、前言 煤气泄漏是一个严重的安全隐患&#xff0c;可能导致火灾、爆炸以及对人体健康的威胁。为了提高家庭和工业环境中煤气泄漏的检测和预防能力&#xff0c;设计了一种基于单片机的防煤气泄漏装置。 单片机选择STC89C52作为主控芯片。为了检测煤气泄漏&#xff0c;采用了…

cocosCreator 调用wxAPI 及后台授权设置、获取用户昵称和头像

版本&#xff1a; 3.8.0 语言&#xff1a; TypeScript 环境&#xff1a; Mac 官方文档&#xff1a; 微信官方文档 - 开放能力 微信 API 小游戏环境 在cocosCreator的3.x版本项目开发中&#xff0c;TypeScript最终会被转换为JavaScript语言。 JavaScript的运行时调用的API…

联发科MT6893(天玑1200)_MTK5G芯片规格参数性能_安卓手机主板方案

联发科天玑1200集成MediaTek 5G调制解调器&#xff0c;通过包含6大维度、72个场景测试的德国莱茵TV Rheinland认证&#xff0c;支持高性能5G连接&#xff0c;带给用户全场景的高品质5G连网体验。 进入5G时代&#xff0c;AI多媒体成为主流应用&#xff0c;天玑1200以强劲的平台…

【高效开发工具系列】你真的会使用Mac吗?

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

uniapp实现瀑布流

首先我们要先了解什么是瀑布流&#xff1a; 瀑布流&#xff08;Waterfall Flow&#xff09;是一种常见的网页布局方式&#xff0c;也被称为瀑布式布局或砌砖式布局。它通常用于展示图片、博客文章、商品等多个不同大小和高度的元素。 瀑布流布局的特点是每个元素按照从上到下…

现代的简洁,诠释轻奢的精致!福州中宅装饰,福州装修

轻奢风是一种生活新时尚 优雅、低调、舒适、简单&#xff0c;不断地推陈出新 站在时尚的前沿&#xff0c;引领潮流 中宅装饰集团轻奢风格产品 追求高品质生活细节 以设计精致的空间构造营造出 一种优雅、时尚生活氛围 将低调奢华之美注入现代家居设计中 客厅|The Sitt…

世界电信日 | 人大金仓助力中国移动租赁核算系统升级上线

世界电信日 5月17日恰逢第五十四个世界电信日&#xff0c;运营商作为新型基础设施建设以及维护网信安全的主力军&#xff0c;掌握关键核心技术&#xff0c;实现科技自立自强刻不容缓。 作为数据库领域国家队&#xff0c;人大金仓坚持原始创新&#xff0c;低难度、低成本、低风…

早安心语微语早读,保持一颗平常心,坐看云起落花开谢得之淡然

1、保持一颗平常心&#xff0c;坐看云起落花开谢得之淡然&#xff0c;失之坦然&#xff0c;让生命中每一天都充满着阳光和希望&#xff01; 2、每个人都一样&#xff0c;都有一段独行的日子&#xff0c;或长或短&#xff0c;这都是无可回避的。不必总觉得生命空空荡荡&#xf…

ArcGIS计算土地现状容积率

本文讲解在ArcGIS中,基于建筑数据和地籍边界数据,计算土地容积率。 一、容积率介绍 容积率(Plot Ratio/Floor Area Ratio/Volume Fraction)是指一个小区的地上建筑总面积与净用地面积的比率。又称建筑面积毛密度。 二、数据分析 (1)建筑数据(dwg) (2)地籍边界数据…

利用AI Chat 将电子书自动截屏并保存成pdf文件

电子书如果要下载下来&#xff0c;无非就两种类型的方法&#xff0c;一种是从内部破解&#xff0c;通常是某些极客将软件破解成免费版&#xff0c;但是风险也大。另一种是从外部破解&#xff0c;就是截屏保存&#xff0c;然后将所有图片拼成pdf文件。 如果要将整本电子书截屏保…

LeetCode刷题---简单组(六)

文章目录 &#x1f352;题目一 69. x 的平方根&#x1f352;解法一&#x1f352;解法二&#x1f352;题目二 70. 爬楼梯&#x1f352;解法一 &#x1f352;题目一 69. x 的平方根 &#x1f352;解法一 class Solution(object):def mySqrt(self, x):""":type x:…

制作一个简单的C语言词法分析程序

1.分析组成 C语言的程序中&#xff0c;有很单词多符号和保留字。一些单词符号还有对应的左线性文法。所以我们需要先做出一个单词字符表&#xff0c;给出对应的识别码&#xff0c;然后跟据对应的表格来写出程序 2.程序设计 程序主要有循环判断构成。不需推理即可产生的符号我…

SSM培训报名管理系统开发mysql数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 SSM 培训报名管理系统是一套完善的信息系统&#xff0c;结合SSM框架完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主 要采用B/S模式开…

Windows2008系统怎么隐藏或打开文件后缀

打开服务器的控制面板-选择小图标-文件夹选项 在文件夹选项那边点击查看-隐藏一直文件类型的扩展名 选择勾选&#xff08;隐藏一直文件类型的扩展名&#xff09;-下图示文件后缀不显示 选择不勾选&#xff08;隐藏一直文件类型的扩展名&#xff09;-下图示文件后缀显示

VsCode 只有一个标签页 编辑区只能打开一个文件

产生如图所示的问题&#xff1a; 可能是不小心取消了勾选 勾选&#xff0c;Show Tabs

前端 : 用HTML ,CSS ,JS 做一个点名器

1.HTML&#xff1a; <body><div id "content"><div id"top"><div id "name">XAiot2302班点名器</div></div><div id "center"><div id "word">你准备好了吗?</di…

【Docker 内核详解】cgroups 资源限制(二):组织结构与基本规则、子系统简介

cgroups 资源限制&#xff08;二&#xff09;&#xff1a;组织结构与基本规则、子系统简介 1.组织结构与基本规则2.子系统简介 1.组织结构与基本规则 在之前的博客已经介绍过&#xff0c;传统的 Unix 任务管理&#xff0c;实际上是先启动 init 任务作为根节点&#xff0c;再由…

Specializing Smaller Language Models towards Multi-Step Reasoning论文精读

0 Abstract 普遍认为&#xff0c;LLM涌现出来的few-shot learning能力是超大参数模型独有的&#xff08;>100B&#xff09;【emergent abilities】&#xff1b;作者认为&#xff0c;小模型&#xff08;<10B&#xff09;可以将这些能力从大模型&#xff08;>100B&…

uniapp中APP端使用echarts用formatter设置y轴保留2位小数点不生效

uniapp使用echarts&#xff0c;在内置浏览器中&#xff0c;设置保留2位小数能正常显示&#xff08;代码如下&#xff09;&#xff0c;但是在APP端这个设置不起作用。 yAxis: {type: value,axisLabel: {formatter: function (val) {return val.toFixed(2); //y轴始终保留小数点…