STL-queue和priority_queue的模拟实现

news2024/9/20 22:53:28

回顾

对于STL,我们已经知道了vector和list,而它们是STL中被称为六大组件之一的容器,我们还学习了模拟实现stack,而stack在STL中被称为六大组件之一的适配器,今天,我们来学习queue的模拟实现和priority_queue的模拟实现,并且,它们也是适配器。

对于适配器而言,我们参照stack的模拟实现的经验,适配器的接口不过是调用容器的接口实现的,所以要实现一个适配器,只要理解了它的数据结构,实现是很简单的。

queue(队列)

什么是队列?在日常生活中,我们如果要去超市买东西,收银的时候如果人比较多,我们就需要排队,从队尾排,直到我们前面的人都收完钱,这就是一个队列,先排的人先走,后排的人后走。队列遵循的是先进先出的原则。

再回过头来看看stack,stack是遵循先进后出的原则,它与我们今天要学的queue相反。

所以,queue的整体还是很好理解的,那么我们如果要实现它,应该采用怎样的数据结构呢? 

stack我们可以使用vector作为容器完成,那么queue呢?是否也能使用vector作为容器完成?

这里就需要考虑到我们的时间复杂度来进行衡量了,因为queue的先进后出的原则,如果我们要进行删除,就只能从队头的数据开始删除,但是如果从队头开始删除数据,vector作为连续的数组空间,那么势必每次删除都需要挪动数据,每次删除数据都将会是一个O(N)的时间复杂度,所以并不高效,也就不能使用vector作为queue的容器。那么大家是否已经想到了比较合适容器比较高效的实现我们的queue呢? -----list和deque就很好,它们对于头删的成本并不高,时间也很高效!

而STL是采用deque作为queue的默认容器。

模拟实现queue

template<typename T , class Container = deque<T>>
class queue
{
public:
	void push(const T& data)
	{
		_data.push_back(data);
	}

	size_t size() const
	{
		return _data.size();
	}

	void pop()
	{
		_data.pop_front();
	}

	bool empty() const
	{
		return _data.empty();
	}
		
	T& front()
	{
		return _data.front();
	}
	const T& front() const 
	{
		return _data.front();
	}

	T& back()
	{
		return _data.back();
	}

	const T& back() const
	{
		return _data.back();
	}
private:
	Container _data;
	};

模版的实现很简单,实现它的接口也只是调用容器的接口,这里也有一个原因说明了了为什么不能使用vector来作为容器,因为vector根本没有pop_front这个接口,也就不可能用来作为我们的容器。

priority_queue(优先级队列)

priority_queue与queue的区别还是挺大的,priority_queue的数据结构其实本质就是堆,对于堆的数据结构而言,它的容器可以为vector,而queue是不可以的。

 对于堆的内容,如果不太理解,可以去查找堆先关的内容,堆是一种基于二叉树的数据结构。

queue的增删是这样的(以大堆举例)

对于删除数据,它会先使堆顶元素(最大元素)与最后的元素交换,然后删除最大元素(尾删),然后再进行堆排序,堆顶元素(本来是尾部的元素)进行向下调整,这一系列操作完成删除操作

对于插入数据,它先在尾部插入数据,然后再进行堆排序

因为它要采用堆的储存方式,所以,我们就必须要实现堆排的两个函数---向上调整和向下调整

向上调整和向下调整都是堆 用于保持堆结构的排序算法

向上调整

用于插入数据时,进行堆排序,保持堆的特性

void adjust_up(size_t pos)    //向上调整
{
	Compare com;
	size_t child = pos;
	size_t parent = (child - 1) / 2;
	while (child > 0)
	{
		//if (_data[parent] < _data[child])
		if (com(_data[parent],_data[child])) //仿函数  等会讲
		{
			std::swap(_data[parent], _data[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

向下调整

void adjust_down(size_t pos)    //向下调整
{
	Compare com;
	size_t parent = pos;
	size_t child = parent * 2 + 1;
	while (child < _data.size())
	{
		if (child + 1 < size() && com(_data[child], _data[child + 1]))
		{
			child = child + 1;
		}
		if (com(_data[parent], _data[child]))  //仿函数
		{
			std::swap(_data[parent], _data[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

这里判断是否向上(下)调整的if判断语句中,用了仿函数的知识,这里暂时不讲,后面再讲,只需要知道这是用来判断是否需要调整。

push

void push(const T& data)
{
	_data.push_back(data);
	adjust_up(size() - 1);
}

当插入数据之后,对新插入的元素进行向上调整,使得保持堆结构。

pop

void pop()     //头部删除
{
	assert(!empty());
	std::swap(_data.front(), _data.back());   
	_data.pop_back();
	adjust_down(0);
}

为什么是采用先交换再尾删的方法呢?因为如果要进行堆排序,使用向上调整的时间复杂度为O(N*logN),而如果使用向下调整它的复杂度为O(N)。

其他函数

bool empty() const
{
	return _data.empty();
}


size_t size() const
{
	return _data.size();
}


const T& top() const 
{
	assert(!empty());
	return _data.front();
}

仿函数

template<typename T , class Container = vector<T>, class Compare = less<T> >
class priority_queue
{
private:
	Container _data;
};

template<typename T>
struct greater
{
public:
	bool operator()(const T& t1, const T& t2) const
	{
		return t1 > t2;
	}
};

在实现priority_queue中,它的模版函数是这样的

template<typename T , class Container = vector<T>, class Compare = less<T> >

前两个不难理解,作为适配器,需要传入容器,那么第三个是什么呢?

在实现刚刚的priority_queue中,既然是堆的方式实现,那么有时候我们就可能需要大堆或者是小堆,这时候,就可以使用仿函数来实现,可为什么叫做仿函数?

仿函数也是一个类,但是你看我们使用它的时候,是不是就像一个函数的调用?

void adjust_up(size_t pos)    //向上调整
{
	Compare com;
	size_t child = pos;
	size_t parent = (child - 1) / 2;
	while (child > 0)
	{
		//if (_data[parent] < _data[child])
		if (com(_data[parent],_data[child])) //仿函数
		{
			std::swap(_data[parent], _data[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

而它在类中是如何实现的?-> 是通过重载()达到这种效果。

template<typename T>
struct greater
{
public:
	bool operator()(const T& t1, const T& t2) const
	{
		return t1 > t2;
	}
};

这就是仿函数类 greater

如果你想实现 仿函数类 less,就是这样写

template<typename T>
struct less
{
public:
	bool operator()(const T& t1, const T& t2) const
	{
		return t1 < t2;
	}
};

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

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

相关文章

分布式运用之ELK企业级日志分析系统

1.ELK的相关知识 1.1 ELK的概念与组件 ELK平台是一套完整的日志集中处理解决方案&#xff0c;将 ElasticSearch、Logstash 和 Kiabana 三个开源工具配合使用&#xff0c; 完成更强大的用户对日志的查询、排序、统计需求。 ElasticSearch&#xff1a; 是基于Lucene&#xff0…

Tomcat服务器、Servlet生命周期、上传下载文件、使用XHR请求数据、注解使用

文章目录 Servlet认识Tomcat服务器使用Maven创建Web项目创建Servlet探究Servlet的生命周期解读和使用HttpServletWebServlet注解详解使用POST请求完成登陆上传和下载文件下载文件上传文件 使用XHR请求数据重定向与请求转发重定向请求转发 ServletContext对象初始化参数 Servlet…

基于自营配送模式的车辆路径规划设计与实现_kaic

摘要 近年来&#xff0c;随着我国消费水平逐渐提升&#xff0c;消费者在网上购物的频率也越来越高&#xff0c;电商发展速度迅猛&#xff0c;加大了物流配送的压力&#xff0c;促使物流企业以更大的运力&#xff0c;更短的时间将货物送达。在货品的运输过程中&#xff0c;成本居…

TCP 连接未必都是用TCP协议沟通

确实&#xff0c;一般来说 TCP 连接是标准的 TCP 三次握手完成的&#xff1a; 1.客户端发送 SYN&#xff1b; 2.服务端收到 SYN 后&#xff0c;回复 SYNACK&#xff1b; 3.客户端收到 SYNACK 后&#xff0c;回复 ACK。 SYN 会在两端各发送一次&#xff0c;表示“我准备好了&…

MicroBlaze系列教程(9):xilisf串行Flash驱动库的使用

文章目录 1. xilisf库简介2. xilisf库函数3. xilisf配置4. xilisf应用示例工程下载本文是Xilinx MicroBlaze系列教程的第9篇文章。 1. xilisf库简介 xilisf库(Xilinx In-system and Serial Flash Library) 是Xilinx 提供的一款串行Flash驱动库,支持常用的 Atmel 、Intel、S…

Linux基本指令介绍

目录 前言 指令操作与图形化界面的对比 adduser passwd whoami ls指令 pwd指令 cd指令 touch指令 mkdir指令 rmdir指令 && rm 指令 man指令 cp指令 mv指令 cat&#xff08;显示文件内容&#xff08;全部&#xff09;&#xff0c;且不可修改的&#xff09;…

【1++的C++初阶】之模板

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的C初阶】 文章目录 一&#xff0c;浅谈模板二&#xff0c;函数模板三&#xff0c;类模板 一&#xff0c;浅谈模板 在前面的文章【【1的C初阶】之C入门篇1】中我们对函数重载有了一定的认识&a…

Python-web开发学习笔记(3):Flask Demo,一个网站开发小案例

☕️ 推荐关注我的另一个专栏系列&#xff1a;大道至简&#xff0c;该系列收录了许多优质的人工智能算法博文&#xff0c;包括机器学习和自然语言处理等&#xff0c;持续更新中&#xff0c;欢迎各位关注~ 介绍完HTML标签&#xff0c;我们来搞一个简单的网站小案例&#xff0c;带…

一、CNNs网络架构-基础网络架构

目录 1.LeNet 2.AlexNet 2.1 激活函数&#xff1a;ReLU 2.2 随机失活&#xff1a;Droupout 2.3 数据扩充&#xff1a;Data augmentation 2.4 局部响应归一化&#xff1a;LRN 2.5 多GPU训练 2.6 论文 3.ZFNet 3.1 网络架构 3.2 反卷积 3.3 卷积可视化 3.4 ZFNet改…

6.4.6拓扑排序

用DAG&#xff08;有向无环图&#xff09;表示一个工程。顶点表示活动&#xff0c;有向边<Vi&#xff0c;Vj>表示活动Vi活动必须先与Vj活动进行。 所谓的拓扑排序&#xff1a;找到做事的先后顺序 以上根据拓扑排序的实现&#xff1a; 加入对有回路的图进行拓扑排序&#…

重磅,ChatGPT App 来了!(附保姆级教程)

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了人工智能中文站https://ai.weoknow.com 每天给大家更新可用的国内可用chatGPT资源 日前&#xff0c;OpenAI宣布推出iOS移动版ChatGPT&#xff0c;为iPhone和iPad用户提供免费下载和使用机会。 总体而言&#xff0…

视频转换芯片MS7200概述 HDMI转数字RGB/YUV/HDMI RXReceive/替代IT66021FN

1. 基本介绍 MS7200 是一款 HDMI 接收芯片&#xff0c;兼容 HDMI1.4b 及 HDMI 1.4b 以下标准的视频 3D 传输格 式&#xff0c;最高分辨率可支持到 4K30Hz&#xff0c;最高采样率达到 300MHz。MS7200 支持 YUV 和 RGB 之 间的色彩空间转换&#xff0c;数字接口支持 YUV 及 RGB 格…

动态规划-完全平方数

动态规划-完全平方数 1 题目描述2 示例2.1 示例 1&#xff1a;2.2 示例 2&#xff1a;2.3 提示&#xff1a; 3 解题思路及方法3.1 解题思路3.1.1 确定状态3.1.2 转移方程3.1.3 初始条件和边界情况3.1.4 计算顺序 3.2 算法代码实现 跟着九章侯老师学习了动态规划专题之后根据学习…

联邦图神经网络:概述、技术和挑战

https://arxiv.org/pdf/2202.07256.pdf 摘要 图神经网络以其处理实际应用中广泛存在的图形数据的强大能力&#xff0c;受到了广泛的研究关注。然而&#xff0c;随着社会越来越关注数据隐私&#xff0c;GNN需要适应这种新常态。这导致了近年来联邦图神经网络&#xff08;FedGN…

一、阿里云oss

文章目录 一、阿里云oss1、开通“对象存储OSS”服务1.1创建Bucket1.2上传默认头像1.3获取用户acesskeys 2、使用SDK文档3、文件服务实现3.1搭建service-oss模块3.1.1 搭建service-oss模块3.1.2 修改配置3.1.3 启动类3.1.4配置网关 3.2 测试SDK3.3封装service接口3.4封装control…

【C++初阶】类和对象(下)构造函数(初始化列表) + explicit关键字 +static成员

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…

如何下载旧版的Chrome

&#xff08;下面网址需要科学上网&#xff09; 1.获得浏览器版本号 访问 https://www.google.com 然后在搜索框中输入要搜索的浏览器版本号&#xff0c;例如 : site:chromereleases.googleblog.com 96.0。其中96.0是大版本号。 2.查询浏览器版本号的具体信息 访问 https://…

CANopenNode Master RPDO 配置

文章目录 CANopenNode 简介CANopenNode 主栈SDO ClientRPDORPDO 通讯参数RPDO 通信参数设置实例PDO 映射参数RPDO 映射参数设置实例 CANopenNode 简介 CANopenNode 是一个开源的免费的开源 CANopen 协议栈。 对象字典为任何变量提供清晰灵活的组织。可以直接或通过读/写函数…

电力系统的虚假数据注入攻击和MTD系统研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

代码随想录算法训练营第十五天|树的层序遍历 、226.翻转二叉树 、101.对称二叉树

层序遍历&#xff08;广度优先遍历&#xff09;&#xff1a; 遍历思路&#xff1a; 借用队列来实现。 若根节点不为空&#xff0c;则先将根节点放入队列&#xff0c; 随后&#xff0c;在while循环中&#xff0c;判断队列当前的size&#xff0c;队列的size就是树在该层中的节点…