【C++】priority_queue模拟实现过程中值得注意的点

2024-01-22 19:12

本文主要是介绍【C++】priority_queue模拟实现过程中值得注意的点,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

👀樊梓慕:个人主页

 🎥个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C++》《Linux》《算法》

🌝每一个不曾起舞的日子,都是对生命的辜负


前言

本篇文章旨在记录博主在模拟实现priority_queue适配器中遇到的一些问题,希望与大家共勉。


欢迎大家📂收藏📂以便未来做题时可以快速找到思路,巧妙的方法可以事半功倍。

=========================================================================

GITEE相关代码:🌟fanfei_c的仓库🌟

=========================================================================


1.priority_queue的介绍

  • priority_queue顾名思义:『 优先级队列』;
  • 人话:

priority_queue就是基于vector容器的堆,所以未来涉及到『 堆的应用』都可以使用priority_queue适配器。

默认为大堆。

同样通过模板参数的方式来决定底层容器和构建小堆。

比如:

  • 使用『 vector』作为底层容器,内部构造『 大堆』结构。
priority_queue<int, vector<int>, less<int>> q1;
  • 使用『 vector』作为底层容器,内部构造『 小堆』结构。
priority_queue<int, vector<int>, greater<int>> q2;
  • 不指定底层容器和内部需要构造的堆结构,采用默认即vector、大堆。
priority_queue<int> q;

比较奇怪的是:

构建大堆要传less,构建小堆要传greater,容易记混。

  • 『 大堆』less就是逐渐变小;
  • 『 小堆』greater就是逐渐变大。

2.堆的回顾

2.1向上调整算法

向上调整算法的前提是『 祖先是堆』。

以小堆为例:

1.给定向上调整的起点(孩子节点下标),根据起点下标计算双亲节点下标。

孩子节点与双亲结点间的下标关系:

  • child=parent*2+1 || child=parent*2+2;
  • parent=(child-1)/2;

2.比较孩子节点与双亲节点数值大小,若孩子节点小于于双亲节点,则交换两者,并将孩子节点的下标更新为之前的双亲节点下标,根据最新的孩子节点下标重新计算双亲节点下标,重复这一过程直到孩子节点为根节点。

//堆的向上调整(小堆)
void AdjustUp(vector<int>& v, int child)
{int parent = (child - 1) / 2; //通过child计算parent的下标while (child > 0)//调整到根结点的位置截止{if (v[child] < v[parent])//孩子结点的值小于父结点的值{//将父结点与孩子结点交换swap(v[child], v[parent]);//继续向上进行调整child = parent;parent = (child - 1) / 2;}else{break;}}
}

2.2向下调整算法

向下调整算法的前提是『 左右子树是堆』。

以小堆为例:

1.给定向下调整的起点(双亲节点下标)和节点总数,根据起点下标计算孩子节点下标。

注意:向下调整时,若有两个孩子节点,则需要确保调整的是较小的孩子节点。

2.比较孩子节点与双亲节点数值大小,若孩子节点小于双亲节点,则交换两者,并将双亲节点的下标更新为之前的孩子节点下标,根据最新的双亲节点下标重新计算孩子节点下标,重复这一过程直到孩子节点超出节点总数。

//堆的向下调整(小堆)
void AdjustDown(vector<int>& v, int n, int parent)
{//child记录左右孩子中值较大的孩子的下标int child = 2 * parent + 1;//先默认其左孩子的值较小while (child < n){if (child + 1 < n && v[child+1] < v[child])//右孩子存在并且右孩子比左孩子还小{child = child + 1;//较小的孩子改为右孩子}if (v[child] < v[parent])//左右孩子中较小孩子的值比父结点还小{//将父结点与较小的子结点交换swap(v[child], v[parent]);//继续向下进行调整parent = child;child = 2 * parent + 1;}else{break;}}
}

3.构建大小堆的模板参数问题『仿函数 』 

这里同样涉及到模板参数的传递问题。

在学习堆时,我们知道可以通过改变比较方式从而实现建大堆或者建小堆:

但我们不能每次都手动的去改变这个符号从而满足用户需求。

所以这里我们同样可以利用『 仿函数』来解决这一问题。

仿函数是一个类或结构体,它重载了operator()运算符,使其可以像函数一样被调用。

仿函数的实例可以像函数指针一样传递给STL算法或容器的操作,从而实现自定义行为。

如同我们今天的例子,_com是less类的实例对象,less类重载了操作符(),使其类似于函数调用,内部实现比较大小的功能返回布尔值,_com()就是仿函数,该仿函数模拟了函数行为,使if的判断条件为真或假,从而达到我们>、<的目的。 

 

  • 函数参数传参是在『 使用时传参』,传递的是『 对象』;
  • 模板参数传参是在『 编译时传参』,传递的是『 类型』。

4.模拟实现源码

#pragma oncenamespace F
{//比较方式:减小堆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;}};template<class T, class Container = std::vector<T>, class Compare = less<T>>class priority_queue{public://向上调整void adjust_up(int child){int parent = (child - 1) / 2;while (child > 0){//通过模板参数Compare确定是否需要交换结点位置if (_com(_con[child],_con[parent])){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}//向下调整void adjust_down(int parent){int child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && _com(_con[child+1],_con[child])){++child;}if (_com(_con[child], _con[parent])){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}void push(const T& x){_con.push_back(x);adjust_up(_con.size() - 1);}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}const T& top(){return _con[0];}size_t size(){return _con.size();}bool empty(){return _con.empty();}private:Container _con;//底层容器Compare _com;//比较方式};
}

=========================================================================

如果你对该系列文章有兴趣的话,欢迎持续关注博主动态,博主会持续输出优质内容

🍎博主很需要大家的支持,你的支持是我创作的不竭动力🍎

🌟~ 点赞收藏+关注 ~🌟

=========================================================================

这篇关于【C++】priority_queue模拟实现过程中值得注意的点的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/633970

相关文章

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

作业提交过程之HDFSMapReduce

作业提交全过程详解 (1)作业提交 第1步:Client调用job.waitForCompletion方法,向整个集群提交MapReduce作业。 第2步:Client向RM申请一个作业id。 第3步:RM给Client返回该job资源的提交路径和作业id。 第4步:Client提交jar包、切片信息和配置文件到指定的资源提交路径。 第5步:Client提交完资源后,向RM申请运行MrAp

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo