C++ priority_queue简单源码剖析:priority_queue模拟实现

文章目录

    • 1. priority_queue介绍
    • 2. priority_queue模拟实现
    • 3. 适配器与虚函数

大家好!本文会用C++模拟一个基本的priority_queue类,帮助我们更好的理解priority_queue的内置函数的实现与规则。

在这里插入图片描述

1. priority_queue介绍

priority_queue被叫做优先队列

  1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
  2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素),其数据结构类似于大堆。
  3. 优先队列被实现为容器适配器容器适配器将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
  4. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:
    • empty():检测容器是否为空
    • size():返回容器中有效元素个数
    • front():返回容器中第一个元素的引用
    • push_back():在容器尾部插入元素
    • pop_back():删除容器尾部元素
  5. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector。
  6. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。

priority_queue数据结构:
在这里插入图片描述

2. priority_queue模拟实现

我们先来看看priority_queue需要实现哪些函数:

    template <class T,class Container = vector<T>, class Compare = less<T>>class priority_queue{public://构造函数priority_queue() = default;template <class InputIterator>priority_queue(InputIterator first, InputIterator last);//容器操作void adjust_up(int child);void adjust_down(int parent);bool empty() const;size_t size() const;const T& top() const;void push(const T& x);void pop();private:Container _c;Compare comp;};
}

priority_queue需要实现的函数很少,而且函数也多为复用 Container类型的内置函数,所以这里不多讲解,直接贴代码。(priority_queue的重点不在它的函数,在本篇的第三个专题)。

构造函数:

 //强制编译器生成默认构造函数priority_queue() = default;//支持迭代器构造template <class InputIterator>priority_queue(InputIterator first, InputIterator last){while (first != last){_c.push_back(*first);first++;};for (int i = ((_c.size() - 1 - 1) / 2); i > 0; i--){adjust_down(i);};};

容器操作函数:

//堆的向上调整函数
void adjust_up(int child)
{while(child > 0){int parent = (child - 1) / 2;if (comp(_c[parent], _c[child])){std::swap(_c[parent], _c[child]);child = parent;}else{break;}}
};//堆的向下调整函数
void adjust_down(int parent)
{while (parent > ((_c.size()-1-1)/2)){int child = parent * 2 + 1;if (comp(_c[child + 1],_c[child])&& child + 1 < _c.size()){child++;}if (comp(_c[parent],_c[child])){std::swap(_c[parent], _c[child]);parent = child;}else{break;}}
};
//复用_c的内置empty函数
bool empty() const
{return _c.empty();
};//复用_c的内置size函数
size_t size() const
{return _c.size();
};//复用_c的内置top函数
const T& top() const
{return _c.front();
};//推入一个数,再对其向上调整
void push(const T& x)
{_c.push_back(x);adjust_up(_c.size() - 1);
};//交换头尾元素(头为最大元素)
//对新头元素向下调整
//对新尾元素pop
void pop()
{if (_c.empty()) return;std::swap(_c[0],_c[_c.size() - 1]);_c.pop_back();adjust_down(0);
};

3. 适配器与虚函数

在上文我们注意到,priority_queue有两个特别模板参数,是模板的第二个和第三个参数,而且,他们是priority_queue两个参数的类型,那么,他们是什么🤔?

template <class T,class Container = vector<T>, class Compare = less<T>>
{//...private:Container _c;Compare comp;
}

我们一个一个说。

第二个模板参数-适配器:在C++中,Container叫做 适配器,它的缺省值是一个模板容器,在 priority_queue中,他的默认适配器就是vector。在整个 priority_queue的模拟实现中,所有的函数都是围绕这个适配器容器进行的,可以看作把vector的数据结构量身定做成堆的数据结构,本身堆就是可以用数组实现的。

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

第二个模板参数-虚函数:在C++中,有一种模板类当做模板参数传入,像函数一样被使用,这样的模板类被称为虚函数,它实际上是类而不是函数。
在这里插入图片描述
当然上文留给个悬念,就是没有去实现这个less类。虚函数的实现有一定的规则,现在我们来实现less类:

template<class T>
class less
{
public://类中只有一个()的运算符重载,这个函数用来做判断bool operator()(const T& a, const T& b){return a > b;}
};

虚函数的实现大概就是这样的模式,然后这个类就可以像函数一样使用。

在这里插入图片描述
因为priority_queue默认是大堆,所以默认判断规则为less类,当然,如果要实现小堆也是可以的,就要实现一个greater类,并像参数一样传入:

//greater类
template<class T>
class greater
{
public:bool operator()(const T& a, const T& b){return a < b;}
};
//声明
priority_queue<int,vector<int>,greater<int>> x;

由此全部完成,附上总代码图:

#pragma oncenamespace bit
{template<class T>class less{public:bool operator()(const T& a, const T& b){return a > b;}};template<class T>class greater{public:bool operator()(const T& a, const T& b){return a < b;}};template <class T,class Container = vector<T>, class Compare = less<T>>class priority_queue{public:priority_queue() = default;template <class InputIterator>priority_queue(InputIterator first, InputIterator last){while (first != last){_c.push_back(*first);first++;};for (int i = ((_c.size() - 1 - 1) / 2); i > 0; i--){adjust_down(i);};};bool empty() const{return _c.empty();};size_t size() const{return _c.size();};const T& top() const{return _c.front();};void push(const T& x){_c.push_back(x);adjust_up(_c.size() - 1);};void pop(){if (_c.empty()) return;std::swap(_c[0],_c[_c.size() - 1]);_c.pop_back();adjust_down(0);};void adjust_up(int child){while(child > 0){int parent = (child - 1) / 2;if (comp(_c[parent], _c[child])){std::swap(_c[parent], _c[child]);child = parent;}else{break;}}};void adjust_down(int parent){while (parent > ((_c.size()-1-1)/2)){int child = parent * 2 + 1;if (comp(_c[child + 1],_c[child])&& child + 1 < _c.size()){child++;}if (comp(_c[parent],_c[child])){std::swap(_c[parent], _c[child]);parent = child;}else{break;}}};private:Container _c;Compare comp;};
}

本文就到这里,感谢你看到这里❤️❤️!
我知道一些人看文章喜欢静静看,不评论🤔,但是他会点赞😍,这样的人,帅气低调有内涵😎,美丽大方很优雅😊,明人不说暗话,要你手上的一个点赞😘!

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

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

相关文章

ESP使用巴法云远程OTA(VScode + Platform io)

ESP使用巴法云远程OTA&#xff08;Platform&#xff09; 什么是OTA&#xff1a; OTA&#xff08;Over-the-AirTechnology&#xff09;即空中下载技术&#xff0c;是通过移动通信的空中接口实现对移动终端设备及SIM卡数据进行远程管理的技术。OTA升级是物联网&#xff08;IOT&am…

【C++初阶学习】第十二弹——stack和queue的介绍和使用

C语言栈&#xff1a;数据结构——栈(C语言版)-CSDN博客 C语言队列&#xff1a;数据结构——队列&#xff08;C语言版&#xff09;-CSDN博客 前言&#xff1a; 在之前学习C语言的时候&#xff0c;我们已经学习过栈与队列&#xff0c;并学习过如何使用C语言来实现栈与队列&…

企业软件产品和服务 之 设计保证安全 七项承诺

1. 引言 公司如何保护自己免受数据泄露的影响&#xff1f;标准答案就是&#xff1a; “启用多因素身份验证”——MTA&#xff08;Enable multifactor authentication&#xff09;。 但是&#xff0c;目前很多公司仍然盲目地只使用密码作为唯一的身份来源。 网络安全的核心是…

深入了解 C 语言 Bug

目录 一、引言二、Bug的定义三、Bug的由来四、Bug的影响五、应对 Bug 的方法六、结论 一、引言 1、在 C 语言的编程世界中&#xff0c;Bug 是一个我们无法回避的话题。 2、Bug&#xff0c;简单来说&#xff0c;就是程序中存在的错误或缺陷。它可以表现为程序运行结果的异常、崩…

步进电机双闭环细分控制(matlab仿真)内含课设等参考文件

1.1 步进电机工作原理 步进电机是一种用电脉冲进行控制&#xff0c;将电脉冲信号转换成相位移的电机&#xff0c;其机械位移和转速分别与输入电机绕组的脉冲个数和脉冲频率成正比,每一个脉冲信号可使步进电机旋转一个固定的角度。脉冲的数量决定了旋转的总角度&#xff0c;脉…

英伟达开源新利器NV-Embed向量模型,基于双向注意力的LLM嵌入模型,MTEB 56项任务排名第一

前言 文本嵌入模型能够将文本信息转化为稠密的向量表示&#xff0c;并在信息检索、语义相似度计算、文本分类等众多自然语言处理任务中发挥着关键作用。近年来&#xff0c;基于解码器的大型语言模型 (LLM) 开始在通用文本嵌入任务中超越传统的 BERT 或 T5 嵌入模型&#xff0c…

企业打款验证API在Java、Python、PHP中的使用教程

随着企业银行账号数量的增加和银行间的连接方式不断丰富&#xff0c;企业在进行资金交易时需要确保所填写的收款方账户信息的准确性和合法性&#xff0c;以避免资金损失和风险。然而&#xff0c;由于银行数量众多、地域分布广泛&#xff0c;不同银行间的账户验证机制和信息交互…

网络编程(八)

网络编程&#xff08;八&#xff09; 数据库数据库的分类基于嵌入式的数据库什么是SQLite?为什么使用SQLite?sqlite3数据库的安装 sqlite3中的点命令.open 数据库文件名字.tables [数据库文件名].schema 表名.database.quit.head on.mode column SQLite数据库中的数据类型SQL…

【Linux】Linux环境基础开发工具_4

文章目录 四、Linux环境基础开发工具gcc和g动静态库的理解 make和MakefileLinux小程序---进度条 未完待续 四、Linux环境基础开发工具 gcc和g 动静态库的理解 动态库的优点&#xff1a;比较节省资源&#xff0c;不会出现太多重复代码。 动态库的缺点&#xff1a;对库的依赖性…

C语言 数组——数组的其他应用之筛法求素数

目录 数组的其他应用 求100以内的所有素数 筛法求100以内的所有素数 自顶向下、逐步求精设计算法 数组的其他应用 求100以内的所有素数 筛法求100以内的所有素数 自顶向下、逐步求精设计算法 step 1&#xff1a;设计总体算法  初始化数组a&#xff0c;使a[2]2, a[3]3,..…

算法 java 排序和查找

排序和查找 冒泡排序&#xff08;稳定&#xff09;选择排序&#xff08;不稳定&#xff09;插入排序&#xff08;稳定&#xff09;希尔排序&#xff08;不稳定&#xff09;归并排序&#xff08;稳定&#xff09;快速排序&#xff08;不稳定&#xff09;堆排序计数排序桶排序基数…

C++设计模式——Adapter适配器模式

一&#xff0c;适配器模式简介 适配器模式是一种结构型设计模式&#xff0c;用于将已有接口转换为调用者所期望的另一种接口。 适配器模式让特定的API接口可以适配多种场景。例如&#xff0c;现有一个名为"Reader()"的API接口只能解析txt格式的文件&#xff0c;给这…

【AR开发-开源框架】使用Sceneform-EQR快速开发AR应用,当前接入了AREngine、ORB-SLAM,可快速地适配不同的安卓设备

Sceneform-EQR Sceneform 概览 Sceneform是一个3D框架&#xff0c;具有基于物理的渲染器&#xff0c;针对移动设备进行了优化&#xff0c;使您可以轻松构建增强现实应用程序&#xff0c;而无需OpenGL。 借助 Sceneform&#xff0c;您可以轻松地在 AR 应用和非 AR 应用中渲染…

Caused by: java.rmi.server.ExportException: Port already in use: 1100;解决方案

Caused by: java.rmi.server.ExportException: Port already in use: 1100; 根据端口号找占用程序的进程号 netstat -ano|findstr 1100 最右边那个数字就是进程号 在任务管理器中 详细信息&#xff0c;点击pid即可按照进程号排序&#xff0c;找到相应的进程&#xff0c;判断…

盲盒小程序预售机制的设计与实施

随着盲盒市场的不断发展&#xff0c;预售机制逐渐成为商家吸引用户、提升销售额的重要手段。本文将探讨盲盒小程序预售机制的设计与实施&#xff0c;以帮助商家更好地满足用户需求并优化库存周转率。 一、预售机制设计原则 在设计预售机制时&#xff0c;商家需要遵循以下几个…

基于 vue-element-template 框架添加 tagsview

1. 需求 vue-element-template 是一个基础模板&#xff0c;默认没有 tagsview。所以要手动添加。 参考最全面的集成方案框架 vue-element-admin &#xff0c;拷贝和修改相关文件到你的项目中。 2. 修改 复制如下文件或文件夹 \src\layout\components\TagsView\src\store\mo…

Docker基础篇之本地镜像发布到阿里云

文章目录 1. 本地镜像发布到阿里云的流程2. 阿里云开发平台3. 将自己的本地镜像推送到阿里云 1. 本地镜像发布到阿里云的流程 阿里云ECS Docker生态如下图所示&#xff1a; 2. 阿里云开发平台 在控制台找到容器和镜像服务&#xff1a; 然后创建一个个人实例&#xff1a; 下面…

号称超级增程电动,领克07EM-P带来技术变革?

近年来&#xff0c;自主品牌在新能源汽车领域百花齐放&#xff0c;尤其是在混合动力市场上&#xff0c;比亚迪的DM-i技术引领了风潮&#xff0c;秦L的一经亮相&#xff0c;整个车圈都沸腾了&#xff0c;“超级混动”的概念深入人心。 各大自主品牌都有了自己的混动平台和技术。…

实战

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 实战一&#xff1a;大乐透号码生成器 使用Random模块模拟大乐透号码生成器。选号规则为&#xff1a;前区在1&#xff5e;35的范围内随机产生不重复的…

大数据—元数据管理

在大数据环境中&#xff0c;元数据管理是确保数据资产有效利用和治理的关键组成部分。元数据是描述数据的数据&#xff0c;它提供了关于数据集的上下文信息&#xff0c;包括数据的来源、格式、结构、关系、质量、处理历史和使用方式等。有效的元数据管理有助于提高数据的可发现…