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;包含…

【数值计算方法】Gauss消元法及其Python/C实现

文章目录 一、基础理论1. 线性方程组2. Gauss消元法的详细步骤3. 注意事项 二、具体计算过程1. 用Gauss 消元法求A的LU分解&#xff0c;并由此求解方程组 Ax ba. 将A进行LU分解。b. 使用LU分解求解方程组Axb 三、代码实现1. Python代码实现2. C语言代码实现 Gauss消元法&#x…

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

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

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

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

Linux学习笔记1-入门

前言&#xff1a;之前的基于单片机的闭环控制步进电机项目其实已经完成了&#xff0c;但很多时间都花在调试和生产上&#xff0c;实在没时间去做总结笔记&#xff0c;现在又开始做新项目了&#xff0c;从单片机到了Linux&#xff0c;想用这个平台来督促自己继续学习&#xff0c…

联发科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 的首页,持续学…

ROS中/odom,/map,/base_link几个坐标系的含义:从ROS的REP105严谨介绍

这两日看lego-loam&#xff0c;其中的坐标系定义很混乱。 在lego-loam的一个issue中&#xff0c;作者指出他坐标系定义是完全按照ROS的REP103和105的建议的。 然后又看到了关于/odom, /map, /base_link 几个坐标系的含义的说明&#xff0c;因此整理一下。 关于ROS的REP 关于R…

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…

react动态插入样式

在开发组件过程中&#xff0c;偶尔需要动态的插入css&#xff0c;比如在在iframe中渲染组件后&#xff0c;iframe中是没有样式的&#xff0c;所以需要手动插入样式。 插入样式 通常是在useLayoutEffect中动态创建style标签 useLayoutEffect(() > {if (!ref.current) {cons…

ArcGIS计算土地现状容积率

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

C语言 写一个简易音乐播放器

#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <math.h>#define SAMPLE_RATE 44100 // 采样率 #define AMPLITUDE 32767 // 振幅 #define NO_SAMPLES 44100 // 样本数// 声明一个结构体用于表示音符 typedef struct {double …

[USACO23OPEN] Field Day S题解

远古的回忆。 把变换一个字符视为边权为 1 1 1 的边&#xff0c;即求最长路。 最长路不好搞&#xff0c;考虑转补集最短路&#xff08;容易感性理解&#xff09;&#xff0c;BFS 即可。 #include<bits/stdc.h> #define int long long using namespace std;const int …

利用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模式开…