【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();
}

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MATLAB仿真通信系统的眼图

eyediagram eyediagram(complex(used_i,used_q),1100)

【Java 进阶篇】Java 中 JQuery 对象和 JS 对象:区别与转换

在前端开发中&#xff0c;经常会涉及到 JavaScript&#xff08;JS&#xff09;和 jQuery 的使用。这两者都是前端开发中非常重要的工具&#xff0c;但它们之间存在一些区别。本文将详细介绍 Java 中的 JQuery 对象和 JS 对象的区别&#xff0c;并讨论它们之间的转换方法。 1. …

Amazon Aurora MySQL 与 Amazon Redshift 的 Zero ETL 集成已全面可用,一起轻松上手!

“数据是应用、流程和商业决策的核心。” 亚马逊云科技数据库、 数据分析和机器学习全球副总裁 Swami Sivasubramanian 如今&#xff0c;客户常用的数据传输模式是建立从 Amazon Aurora 到 Amazon Redshift 的数据管道。这些解决方案能够帮助客户获得新的见解&#xff0c;进而…

【C/C++笔试练习】内联函数、函数重载、调用构造函数的次数、赋值运算符重载、静态成员函数、析构函数、模板定义、最近公共祖先、求最大连续bit数

文章目录 C/C笔试练习选择部分&#xff08;1&#xff09;内联函数&#xff08;2&#xff09;函数重载&#xff08;3&#xff09;调用构造函数的次数&#xff08;4&#xff09;赋值运算符重载&#xff08;5&#xff09;静态成员函数&#xff08;6&#xff09;调用构造函数的次数…

微信小程序和H5之间互相跳转、互相传值

微信小程序和内嵌 H5 之间来回跳转&#xff0c;来回交互。 1 微信小程序跳转 H5 1.2. web-view 微信小程序官方提供了 web-view 组件来实现微信小程序跳转到 H5 页面&#xff0c;实现的方式也很简单&#xff0c;具体实现方式如下&#xff1a; 1、新建一个页面用来单独存放 we…

网页推理游戏

目录 python challenge &#xff08;0&#xff09; &#xff08;1&#xff09; &#xff08;2&#xff09; The Riddle &#xff08;1&#xff09; &#xff08;2&#xff09; &#xff08;3&#xff09; &#xff08;4&#xff09; Nazo &#xff08;1&#xff09;…

宋浩高等数学笔记(三)微分中值定理

首先是考研大纲包含的内容&#xff1a; 1.理解并会用罗尔(Rolle)定理、拉格朗日(Lagrange)中值定理和泰勒(Taylor)定理&#xff0c;了解并会用柯西(Cauchy)中值定理. 2.掌握用洛必达法则求未定式极限的方法. 3.理解函数的极值概念&#xff0c;掌握用导数判断函数的单调性和求函…

事务AOP

1事务&#xff1a; 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位。事务会把所有的操作作为一个整体&#xff0c;一起向数 据库提交或者是撤销操作请求。所以这组操作要么同时成功&#xff0c;要么同时失败。 1.1实现&#xff1a;Transactional注解 Transact…

基于SSM的网络书店商城

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

电脑想要微信多开——打开多个微信的必胜法宝!

一个不知名大学生&#xff0c;江湖人称菜狗 original author: Jacky Li Email : 3435673055qq.com Time of completion&#xff1a;2023.11.11 Last edited: 2023.11.11 导读&#xff1a;在生活当中经常遇到工作和生活相撞的事情&#xff0c;导致在处理私人的事情同时不得不处理…

分销cps外卖券电影票小程序开发

电影票外卖劵分销CPS小程序开发作 我们致力于为消费者提供优质、便捷的外卖服务。现在&#xff0c;我们推出全新的电影票外卖劵分销CPS小程序&#xff0c;以及更多具有深度和专业度的功能和服务&#xff0c;以满足消费者更高的生活服务需求。 首先&#xff0c;我们的分销模式…

服务日志性能调优,由log引出一系列的事故

只有被线上服务问题毒打过的人才明白日志有多重要&#xff01; 谁赞成&#xff0c;谁反对&#xff1f;如果你深有同感&#xff0c;那恭喜你是个社会人了&#xff1a;&#xff09; 日志对程序的重要性不言而喻&#xff0c;轻巧、简单、无需费脑&#xff0c;程序代码中随处可见…

Python 使用tkinter复刻Windows记事本UI和菜单功能(一)

下一篇&#xff1a;Python 使用tkinter复刻Windows记事本UI和菜单&#xff08;二&#xff09;-CSDN博客 介绍&#xff1a; Windows操作系统中自带了一款记事本应用程序&#xff0c;通常用于记录文字信息&#xff0c;具有简单文本编辑功能。Windows的记事本可以新建、打开、保…

探索云世界的无限可能

文章目录 每日一句正能量前言云计算的定义和现状云计算能做什么&#xff1f;云计算市场的新特征需求方向&#xff1a;云计算的基础服务已经稳固&#xff0c;行业解决方案是新的发力点模式方向&#xff1a;分布式云模式方向&#xff1a;边缘计算是一朵新的云技术方向&#xff1a…

SQL 聚合函数

前言 SQL中的聚合函数是对一组值执行计算&#xff0c;并返回单个值的函数。 常用的聚合函数有&#xff1a; 函数作用AVG&#xff08;&#xff09;求平均值MAX&#xff08;&#xff09;求最大值MIN&#xff08;&#xff09;求最小值SUM&#xff08;&#xff09;求和COUNT&…

若依vue-初步下载使用

若依框架可以满足大部分的后台管理系统的开发,使用频率也是比较高的,所以这里讲一下如何使用若依框架 若依框架代码克隆 首先去若依官网 http://www.ruoyi.vip/ 这里演示的是若依-vue版本的使用 我们点击下载 会跳转到码云仓库 或者直接点击下面的链接去码云仓库 https://git…

第一章《补基础:不怕学不懂微积分》笔记

微积分包含众多知识点&#xff0c;例如极限概念、求导公式、乘积法则、链式法则、隐函数求导、 积分中值定理、泰勒公式等。其中&#xff0c;研究导数、微分及其应用的部分一般称为微分学&#xff0c;研究不定积分、定积分及其应用的部分一般称为积分学。微分学和积分学统称为微…

动态规划学习——多状态dp(打家劫舍问题)

一&#xff0c;打家劫舍I 题目&#xff1a; 一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响小偷偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自…

Gold-YOLO最新YOLO系列模型

论文地址https://arxiv.org/pdf/2309.11331.pdf 代码地址https://github.com/huawei-noah/Efficient-Computing 目录 01论文介绍 01摘要 02模型训练过程 01安装环境 02修改train中参数 01修改--data-path参数 02修改--conf-file参数 03其他参数设置 03训练 04出现问…

CA 陪你看 Ignite | 聚焦 Microsoft Ignite 2023

点击蓝字 关注我们 编辑&#xff1a;Alan Wang 排版&#xff1a;Rani Sun 微软 Reactor 为帮助广开发者&#xff0c;技术爱好者&#xff0c;更好的学习 .NET Core, C#, Python&#xff0c;数据科学&#xff0c;机器学习&#xff0c;AI&#xff0c;区块链, IoT 等技术&#xff0…