【C++】-stack和queue的具体使用以及模拟实现(dqeue的介绍+容器适配器的介绍)

在这里插入图片描述
💖作者:小树苗渴望变成参天大树🎈
🎉作者宣言:认真写好每一篇博客💤
🎊作者gitee:gitee✨
💞作者专栏:C语言,数据结构初阶,Linux,C++ 动态规划算法🎄
如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧!

文章目录

  • 前言
  • 一、stack
  • 二、queue
  • 三、哪种容器适配stack和queue
  • 四、容器适配器
    • 4.1deque的介绍
    • 4.2deque的优缺点
    • 4.3为什么选择deque作为stack和queue的底层默认容器
  • 五、总结


前言

我们前面几篇介绍了两个常见容器的具体使用和模拟实现,对底层应该是了解的七七八八了,今天学的栈和队列就相对来说比较简单,因为他是一个容器适配器,使用前面两个中的一种来模拟实现就好了,它的功能只是前面两个容器的特殊情况,而栈和队列的接口也相对来说比较的少,一会我会先通过库里面的接口来大家认识一下栈和队列有哪些接口,并且让大家更好的知道什么是容器适配器


一、stack

在这里插入图片描述
传什么容器,栈底层就使用什么容器进行实现,这个到模拟实现的再说。
创建对象:

stack<int> s;
stack<int,vector<int>> s1;
stack<int,list<int>> s2;
//这样的都可以,符合模板参数的就可以

在这里插入图片描述
我们库里面的栈就实现了这几个接口,因为根据栈的特性,我们只需要这几个功能函数就够了

构造函数
在这里插入图片描述

用什么容器进行构造,就要使用什么容器的适配器

vector<int> v(4, 10);
stack<int, vector<int>> s(v);
stack<int> s(v);//这样是错误的,因为默认适配的容器是deque的。

接下里看看各个功能

	vector<int> v(4, 10);
	stack<int, vector<int>> s(v);
	s.push(1);
	s.push(2);
	s.push(3);
	s.push(4);//入栈
	cout << s.size() << endl;//计算栈里面多少个元素
	while (!s.empty())//判断栈为不为空
	{
		cout << s.top() << " ";//取栈顶的元素
		s.pop();//出栈
	}

在这里插入图片描述

强调一点的是,我们的容器适配器要符合结构规则,再数据结构初阶,我们提到栈和队列都可以使用顺序表或者链表,但是分析之后,栈更合适用顺序表结构,队列更适合用链表结构,再库里面有的时候强制进行适配可能会出错,就好比队列,你要传vector容器就会报错,因为底层实现用的是list和deque共同的接口,而vector没有,才会导致出错,适配的本质就是传什么容器就用什么容器来模拟此容器,你也不能传一个树形结构的容器,这样肯定不行,所以大家这点要注意。

通过看源码来分析:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
我们的c就是我们的容器,因为我们的deque,vector和list都有这些功能的接口,而且函数功能都是一样的,如果你传进来的容器没有此上面的函数接口就会报错。,接下来看queue就明白了

二、queue

在这里插入图片描述

创建对象:

queue<int> s;
queue<int,list<int>> s1;
queue<int,vector<int>> s2;//上面两个都可以,符合模板参数的就可以
//第三个就会出现问题,因为vector没有对应的接口

在这里插入图片描述
我们来看底层
在这里插入图片描述
我们看到这个pop_front再vector这个容器里面是没有这个接口的,但是再deque和list容器都有这个pop_frint接口的,所以不会报错,也明白我上面说的传的容器要适配此容器的功能特点

我们再来看看queue这个容器有哪些接口:
在这里插入图片描述
在这里插入图片描述
对于使用来说都是非常的简单,再数据结构初阶的时候就已经介绍过每个接口的含义了

三、哪种容器适配stack和queue

这就要通过stack和queue的特性做决定了。
栈:

我们的栈是先进后出的操作,都是再栈顶进行操作,就是在一个结构尾部进行操作,那我们的vector和list对于尾部的插入和删除的时间复杂度是一样的,使用vector会更好一些,因为vector是一段连续的内存空间,空间利用率高,空间碎片少。所以使用vector去适配栈结构会比较好一些

模拟实现:

#include<iostream>
#include<vector>
#include<list>
using namespace std;
namespace xdh
{
	template<class T, class Container = vector<T>>
	class stack
	{
	public:
		stack() {}//可以不用写,因为成员变量是自定义类型,stack默认生成的构造函数会去调用自定义类型的构造函数,
				 //之前说到六大默认成员函数都是一样的道理

		void push(const T& val)//入栈
		{
			_c.push_back(val);
		}
		void pop()//出栈
		{
			_c.pop_back();
		}

		T& top()//去栈顶元素
		{
			return _c.back();
		}
		const T& top() const
		{
			return _c.back();
		}

		size_t size() const//计算栈里面有多少个元素
		{
			return _c.size();
		}

		bool empty() const//判断栈是否为空
		{
			return _c.empty();
		}
	private:
		Container _c;
	};
}

	xdh::stack<int> s;
	xdh::stack<int,vector<int>> s;
	xdh::stack<int, list<int>> s;
	//这三种都是可以的

队列

我们的队列是先进先出的操作,我们的插入在队尾,删除在队头,对于这两个位置的插入和删除,vector在开头删除数据的代价非常大,虽然我们有时候需要取出队头队尾的数据,对于vector支持随机访问,所以这两个位置的数据非常好取出来,但是对于list这两个位置也非常好取出来,而list对于头部的删除操作效率非常高,相比较而言我们的队列使用list去适配会更好,而库里面直接是抹杀了vector这种适配,可以见到vector的头删的效率是多么低的,但是我们在模拟实现的时候可以改变接口函数,强制适配一下看看

模拟实现:

#include<iostream>
#include<vector>
#include<list>
using namespace std;
namespace xdh
{
	template<class T,class Container=list<T>>
	class queue
	{
	public:
		queue(){}//可以不用写,因为成员变量是自定义类型,queue默认生成的构造函数会去调用自定义类型的构造函数,
		         //之前说到六大默认成员函数都是一样的道理
		void push(const T& x)//入队列
		{
			_c.push_back(x);
		}
		void pop() //出队列
		{
			//代码1
			_c.erase(_c.begin());//为了强制和vector进行适配
			
			//代码2
			//_c.pop_front();//库里面是这样的
		}
		T& back()//取队尾的数据
		{ 
			return _c.back();
		}
		const T& back()const
		{ 
			return _c.back(); 
		}
		T& front() //取队头的数据
		{ 
			return _c.front();
		}
		const T& front()const 
		{ 
			return _c.front();
		}
		size_t size()const //计算队列的大小
		{ 
			return _c.size(); 
		}
		bool empty()const //判断队列是否为空
		{ 
			return _c.empty(); 
		}
	private:
		Container _c;
	};
}

我们将pop里面的代码换成代码1就可以强制适配vector容器了,但是库里面实现的代码2的风格,所以使用vector适配就会报错

四、容器适配器

前面提到好多次关于容器适配器,它具体是什么呢??

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

在这里插入图片描述

虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配器,这是因为stack和队列只是对其他容器的接口进行了包装,STL中stack和queue默认使用deque,比如:
在这里插入图片描述
在这里插入图片描述
我们发现从一开始我们的库里面实现的都不是像我们一开始默认使用的容器,他是使用deque的容器,当成栈和队列的默认容器,deque是什么,为什么选择它做默认适配器,我们来看看。

4.1deque的介绍

首先通过前面的的分析,想要成为stack和queue的适配容器,该有的优点不能少,不然就直接选择我刚才说到两种容易作为默认的不就行了,既然使用的deque,那么它肯定是有很多的优点,我们一起来看看:

deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
这么一看我们的deque好像是vector和list的结合体,支持头插头删,也支持随机访问,看上去是非常好的,但是当我们深入的去看的时候就发现也就那样,不然不就可以直接替代vector和list了嘛

我们来看deque的具体结构是啥样的:
在这里插入图片描述
我们在来画图分析怎么存放数据的:
在这里插入图片描述

通过上面的图,我们发现虽然可以随机访问数据,但是要找到在哪个小的空间上,并且找到在哪个位置才能进行访问,而对于中间的插入是不友好的,是往满的空间进行扩容插入,还是想你开一个小的空间,那么指针数组的位置的值就要挪动,总之这样办法都是不好的。但是对于头插头删或者尾插尾删是优化的

通过画图我们发现一小段的空间并不是连在一起的,按照STL的原则,遍历都可以使用迭代器,而且每个容器的用法都是一样的,那么那deque是如何借助其迭代器维护其假想连续的结构呢?

在这里插入图片描述

他的迭代器有四个指针,所以内部是非常的复杂,而且遍历的时候需要检查是否到达边界
在这里插入图片描述
通过源码我们需要检查小空间的边界条件,这样就导致效率低下

4.2deque的优缺点

与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。

与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。

但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构==

4.3为什么选择deque作为stack和queue的底层默认容器

stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构,都可以作为stack的底层容器,比如vector和list都可以;queue是先进先出的特殊线性数据结构,只要具有push_back和pop_front操作的线性结构,都可以作为queue的底层容器,比如list。但是STL中对stack和queue默认选择deque作为其底层容器,主要是因为:

  1. stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行操作。
  2. 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,deque不仅效率高,而且内存使用率高。

结合了deque的优点,而完美的避开了其缺陷

对于库里面的模拟是西安给的默认容器就是deque

五、总结

对于栈和队列的底层原理大家应该都清楚了吧,deque作为了解就可以,不需要深究,下一篇我将通过几个题目来让大家更好的使用栈和队列,我们下篇再见
请添加图片描述

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

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

相关文章

TCP四次挥手过程

TCP 断开连接是通过四次挥手方式。 双方都可以主动断开连接&#xff0c;断开连接后主机中的「资源」将被释放&#xff0c; 刚开始双方都处于 establised 状态&#xff0c;假如是客户端先发起关闭请求&#xff0c;过程如下图&#xff1a; 第一次挥手&#xff1a;客户端打算关闭…

【机器学习】基于卷积神经网络 CNN 的猫狗分类问题

文章目录 一、卷积神经网络的介绍1.1 什么是卷积神经网络1.2 重要层的说明1.3 应用领域二、 软件、环境配置2.1 安装Anaconda2.2 环境准备 三、猫狗分类示例3.1 图像数据预处理3.2 基准模型3.3 数据增强3.4 dropout层四、总结 一、卷积神经网络的介绍 1.1 什么是卷积神经网络 …

师承AI世界新星|7天获新加坡南洋理工大学访学邀请函

能够拜师在“人工智能10大新星”名下&#xff0c;必定可以学习到前沿技术&#xff0c;受益良多&#xff0c;本案例中的C老师无疑就是这个幸运儿。我们只用了7天时间就取得了这位AI新星导师的邀请函&#xff0c;最终C老师顺利获批CSC&#xff0c;如愿出国。 C老师背景&#xff1…

基于单片机的盲人导航智能拐杖老人防丢防摔倒发短息定位

功能介绍 以STM32单片机作为主控系统&#xff1b; OLED液晶当前实时距离&#xff0c;安全距离&#xff0c;当前经纬度信息&#xff1b;超声波检测小于设置的安全距离&#xff0c;蜂鸣器报警提示&#xff1a;低于安全距离&#xff01;超声波检测当前障碍物距离&#xff0c;GPS进…

【分布式系统案例课】查询服务设计、计数栈选型、总结

查询服务设计 数据获取路径 两个问题考虑&#xff1a; 1、老数据归档的问题。 如果所有分钟小时级的数据一直存在这个DB当中&#xff0c;那么DB的存储空间会被不断的消耗&#xff0c;性能也会不断的下降。所以一旦小时天月的数据聚合完成&#xff0c;我们就可以将一些老的原始…

TCP/IP网络编程 第十二章:I/O复用

基于I/O复用的服务器端 多进程服务器端的缺点和解决方法 为了构建并发服务器&#xff0c;只要有客户端连接请求就会创建新进程。这的确是实际操作中采用的种方案&#xff0c;但并非十全十美&#xff0c;因为创建进程时需要付出极大代价。这需要大量的运算和内存空间&#xff…

智慧校园能源管控系统

智慧校园能源管控系统是一种搭载了物联网技术、大数据技术、大数据等技术性智能化能源管理方法系统&#xff0c;致力于为学校提供更高效、安全性、可信赖的能源供应管理和服务。该系统包括了校内的电力工程、水、气、暖等各类能源&#xff0c;根据对能源的实时检测、数据统计分…

uni-app中a标签下载文件跳转后左上角默认返回键无法继续返回

1.首先使用的是onBackPress //跟onShow同级别 onBackPress(option){ uni.switchTab({ url:/pages/....... return true }) }发现其在uni默认头部中使用是可以的 但是h5使用了"navigationStyle":"custom"后手机默认的返回并不可以&#xff0c; 2.经过查询…

【MySQL技术专题】「问题实战系列」深入探索和分析MySQL数据库的数据备份和恢复实战开发指南(备份+恢复篇)

深入探索和分析MySQL数据库的数据备份和恢复实战开发指南 MySQL数据库备份全量备份全量备份应用场景 增量备份binlogbinlog主要作用binlog的作用主要有两个方面 开启binlog日志功能要开启MySQL的binlog日志步骤 mysqlbinlogmysqlbinlog的使用案例 全量备份与增量备份结合按天全…

WebRTC不同方案对比

1.功能上会有一些出入&#xff0c;尤其是国内的metaRTC版本迭代很快&#xff0c; 2.后续的ffmpeg也在进行支持webrtc特性&#xff0c;obs新的版本好像已经支持了webrtc&#xff0c; 3.对于webrtc部分缺少的信令部分的标准化也有了对应的标准whip和whep协议 所以&#xff0c;如…

好的CRM需要有哪些特点?

CRM客户管理系统在企业中占有举足轻重的地位&#xff0c;既是战略工具又可以强化部门间的团队协作、优化销售流程、缩短销售周期。市面上crm做得比较好的公司有哪些&#xff1f; 1.上榜Gartner魔力象限 好的CRM市场的引领、产品研发的持续投入、技术创新以及不断增长的市场份…

性能测试:Jmeter-Beanshell请求加密实例

目录 1. 打包加密方法Jar包&#xff0c;导入Jmeter 2. 加密参数 总结&#xff1a; 进行性能测试时&#xff0c;有可能遇到一种场景&#xff1a;接口请求由于安全问题&#xff0c;需要进行加密发送。 这种场景下&#xff0c;使用Jmeter实现性能测试&#xff0c;则也需要使用…

自学网络安全究竟该从何学起?

一、为什么选择网络安全&#xff1f; 这几年随着我国《国家网络空间安全战略》《网络安全法》《网络安全等级保护2.0》等一系列政策/法规/标准的持续落地&#xff0c;网络安全行业地位、薪资随之水涨船高。 未来3-5年&#xff0c;是安全行业的黄金发展期&#xff0c;提前踏入行…

Redis基本全局命令(含key过期策略)

Redis基本全局命令 KEYEXISTSDELEXPIRETTLRedis的key过期策略TYPE KEY 返回所有满⾜样式&#xff08;pattern&#xff09;的key。⽀持如下统配样式。 h?llo 匹配 hello , hallo 和 hxlloh*llo 匹配 hllo 和 heeeelloh[ae]llo 匹配 hello 和 hallo 但不匹配 hilloh[^e]llo 匹配…

使用Pandas计算两个系统客户名称的相似度

引言&#xff1a; 在日常业务处理中&#xff0c;我们经常会面临将不同系统中的数据进行匹配和比对的情况。特别是在涉及到客户管理的领域&#xff0c;我们需要确保两个系统中的客户记录是准确、一致和无重复的。 本文将介绍如何使用Python的Pandas库来处理这个问题。我们将以…

美颜SDK与动态贴纸技术的发展趋势:向更智能、更新颖的美化

美颜SDK和动态贴纸技术在近年来迅速发展&#xff0c;成为移动应用、社交媒体和视频直播等领域中不可或缺的元素。本文将探讨美颜SDK和动态贴纸技术的最新发展趋势&#xff0c;包括智能化算法的应用、增强现实的融合以及个性化定制的兴起。我们将展望未来&#xff0c;展示这些技…

STM32(HAL库)通过ADC读取MQ2数据

目录 1、简介 2、CubeMX初始化配置 2.1 基础配置 2.1.1 SYS配置 2.1.2 RCC配置 2.2 ADC外设配置 2.3 串口外设配置 2.4 项目生成 3、KEIL端程序整合 3.1 串口重映射 3.2 ADC数据采集 3.3 主函数代 3.4 效果展示 1、简介 本文通过STM32F103C8T6单片机通过HAL库方式对M…

注释气泡图函数(更新)

之前我们写过一个原创可视化函数Dotplot_anno.R&#xff0c;nature级别图表&#xff1a;一个注释气泡热图函数&#xff08;适用于单细胞及普通数据&#xff09;。主要解决的问题是1) 单细胞基因可视化分组注释。2) Bulk RNA差异基因热图、气泡图。3) 富集分析结果气泡图展示。这…

【案例实战】高并发业务的多级缓存架构一致性解决方案

我们在高并发的项目中基本上都离不开缓存&#xff0c;那么既然引入缓存&#xff0c;那就会有一个缓存与数据库数据一致性的问题。 首先&#xff0c;我们先来看看高并发项目里面Redis常见的三种缓存读写模式。 Cache Aside 读写分离模式&#xff0c;是最常见的Redis缓存模式&a…

JVM理论(五)执行引擎--解释器/JIT编译器

概述 首先执行引擎是java虚拟机核心的组成部分之一;而JVM的主要任务是装载字节码到内存,但不能够直接运行在操作系统之上.因为字节码指令并非等价于本地机器指令,它仅仅包含能够被JVM所识别的指令、符号表、以及其他信息;而此时执行引擎就华丽登场,它的任务就是将字节码指令解…
最新文章