【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.mzph.cn/news/3404.shtml

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

相关文章

Java八股文总结(二)

Java八股文总结&#xff08;续&#xff09; 接上篇笔记&#xff1a;Jhttps://blog.csdn.net/weixin_44780078/article/details/130192373 文章目录 Java八股文总结&#xff08;续&#xff09;六、MySql 相关1. InnoDB 与 MyISAM 的区别&#xff1f;2. 为什么 InnoDB 存储引擎表…

JavaFx 用户界面控件2——ListView

1.列表显示ListView 下面是一个JavaFX ListView的示例代码和使用方法&#xff1a; public class ListViewExample extends Application {Overridepublic void start(Stage primaryStage) {// 创建一个可观察的列表&#xff0c;用于存储ListView中的数据ObservableList<Str…

【MongoDB】SpringBoot整合MongoDB

【MongoDB】SpringBoot整合MongoDB 文章目录 【MongoDB】SpringBoot整合MongoDB0. 准备工作1. 集合操作1.1 创建集合1.2 删除集合 2. 相关注解3. 文档操作3.1 添加文档3.2 批量添加文档3.3 查询文档3.3.1 查询所有文档3.3.2 根据id查询3.3.3 等值查询3.3.4 范围查询3.3.5 and查…

UML与SYSML的关系

UML与SysML的联系 UML&#xff08;统一建模语言&#xff09;和SysML&#xff08;系统建模语言&#xff09;是两种与建模相关的语言&#xff0c;它们之间存在联系和区别。 SysML的图分类如下图所示。 联系 SysML是基于UML的&#xff0c;它重用了UML 2的子集&#xff0c;并提…

wordpress后台的路径都是空白,进不了后台怎么办?

主题或插件冲突&#xff1a;某些主题或插件可能与其他已安装的主题或插件不兼容&#xff0c;导致登录页面显示为空白。您可以通过禁用所有插件和将主题更改为默认主题来查看是否解决了问题。如果解决了问题&#xff0c;则可以逐个重新启用插件以找出是哪个插件造成冲突。 PHP错…

CGT Asia嘉年华|3D细胞培养与类器官研发峰会10月广州召开

类器官指利用成体干细胞或多能干细胞进行体外三维&#xff08;3D&#xff09;培养而形成的具有一定空间结构的组织类似物&#xff0c;是近10年来干细胞领域发展最快的研究热点之一。2022年&#xff0c;FDA 通过现代化法案 2.0&#xff0c;批准全球首个完全基于“类器官芯片”研…

Win11中的Swapfile.sys

除了 pagefile.sys 和 hiberfil.sys 文件外&#xff0c;在系统根目录会多出一个 swapfile.sys 虚拟内存文件。Windows 10/8 系统为什么会同时使用 SWAP 交换文件和 Page 页面文件呢&#xff1f; 其实 swapfile.sys 文件目前只被用来交换 Universal App (其实就是Metro App)的个…

java ajax

1.ajax定义:异步刷新技术 2.ajax语法 3.ajax实战 在不需要点击刷新按钮时&#xff0c;达到局部刷新的效果&#xff0c;如下图所示 步骤一&#xff1a;创建工程/包/js 步骤二&#xff1a;数据库/表创建 步骤三&#xff1a;实体类 步骤四&#xff1a;UserDao package cn.kgc…

音频数据分割单独处理后再拼接出现跳跃间断点的处理方法

+hezkz17进数字音频系统研究开发交流答疑 1如图所示 问题1: 对于一个81920字节的音频文件,如果是分割成小块4096输入(无重叠,均分),在频域上做去噪算法,每4k数据返回到时域上再拼接成80k的处理结果文件,发现处理结果有异常有跳跃间断点,像是频谱泄露?分割也需要有重…

计算机网络 day9 DNAT实验

目录 DNAT DNAT策略的典型应用环境 DNAT策略的原理 在网关中使用DNAT策略发布内网服务器 DNAT实验&#xff1a; 实验环境&#xff1a; DNAT网络规划拓扑图&#xff1a; 步骤&#xff1a; 1、创建linux客户端Web网站&#xff08;go语言&#xff09;&#xff0c;实现Web服…

如何快速爬取国内985大学学术学报pdf文件

背景 最近&#xff0c;在爬取关于国内985大学的学报时&#xff0c;我注意到大部分大学学报站点格式都采用相似的形式&#xff0c;并且PDF链接都使用自增的ID。然而&#xff0c;我也发现了一个问题&#xff0c;即大多数PDF链接的ID并不是连续的。现在我将向你分享一些方法&…

15 大模型训练 内存优化

先看GPU结构&#xff0c;我们常说显存的时候&#xff0c;说的一般就是Global memory 训练的过程中&#xff0c;我们为了反向传播过程&#xff0c;必须将中间的结果&#xff08;激活值&#xff09;存储下来。 在训练的过程中&#xff0c;那些会消耗内存呢&#xff1f; model we…

web-报错注入

必要的函数 rand select rand(0) from hackbiao; rand(0)&#xff1a;生成以0开头的随机数&#xff0c;生成的数量与字段下数据的条数相等。如果i没有这个地段的话&#xff0c;就会自己形成一个新的字段打印出来。 count和group by grouip by在进行排序的时候&#xff0c;会…

【分布式应用】Ceph的实战应用

目录 一、创建 CephFS 文件系统 MDS 接口1.1服务端操作1&#xff09;在管理节点创建 mds 服务2&#xff09;查看各个节点的 mds 服务3&#xff09;创建存储池&#xff0c;启用 ceph 文件系统4&#xff09;查看mds状态&#xff0c;一个up&#xff0c;其余两个待命&#xff0c;目…

SpringCloud(五)Gateway 路由网关

一、路由网关 官网地址&#xff1a;https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/ 我们需要连接互联网&#xff0c;那么就需要将手机或是电脑连接到家里的路由器才可以&#xff0c;而路由器则连接光猫&#xff0c;光猫再通过光纤连接到互联网&a…

对 Jenkins+ANT+Jmeter 接口测试的实践

目录 1、前言 2、框架与数据准备 3、脚本设计 4、整理测试报告 1、前言 JenkinsANTJMeter是一种常见的接口测试实践方案&#xff0c;可以实现自动化的接口测试和持续集成。Jenkins是一个流行的持续集成工具&#xff0c;ANT是一个构建工具&#xff0c;而JMeter是一个功能强大…

JS逆向系列之猿人学爬虫第18题-jsvmp - 洞察先机

文章目录 目标网址加密参数分析Python 实现往期逆向文章推荐目标网址 https://match.yuanrenxue.cn/match/18题目标着难度是困难级别,主要还是vmp保护的JS代码调试困难,理清逻辑就会变得简单了 加密参数分析 请求第一页时没有加密参数,从第二页开始,url会携带t和v两个参数…

240. 搜索二维矩阵 II

题目描述&#xff1a; 主要思路&#xff1a; 利用矩阵中的单调性进行搜索。 class Solution { public:bool searchMatrix(vector<vector<int>>& matrix, int target) {int nmatrix.size(),mmatrix[0].size();int in-1,j0;while(i>0&&j<m){if(m…

详解CPU的态

目录 1.CPU的工作过程 2.寄存器 3.CPU的上下文 4.系统调用 5.CPU的态 1.CPU的工作过程 CPU要执行的指令的地址存在寄存器中&#xff0c;指令存放在内存中&#xff0c;而CPU本质上就是一个去内存中根据地址取指令&#xff0c;然后执行指令的硬件。 举一个例子&#xff1a…

【蓝图】p27开关门互动实现

p27开关门互动实现 创建一个门 添加初学者内容包 拖拽一个门到场景中 添加一个碰撞 创建盒体触发器 左侧模式->基础->盒体触发器&#xff0c;拖拽到门上&#xff0c;调整大小 开关门互动实现 做一个开门互动 要把开门逻辑写在关卡蓝图里 门设置为可移动 打开关卡蓝…