【C++】STL之适配器---用deque实现栈和队列

news2025/1/17 0:11:48

目录

前言

一、deque

 1、deque 的原理介绍

 2、deque 的底层结构

 3、deque 的迭代器

 4、deque 的优缺点

  4.1、优点

  4.2、缺点

二、stack 的介绍和使用

 1、stack 的介绍

 2、stack 的使用

 3、stack 的模拟实现

三、queue 的介绍和使用

 1、queue 的介绍 

 2、queue 的使用

 3、queue 的模拟实现


前言

  容器适配器,按字面意思理解的话,就是用来对一个容器进行匹配的。在C++STL中,容器有:vector,list,deque,map,set等。而在C++STL中不把stack和queue纳入容器的范围而是纳入容器适配器的范围是因为:

  stack和queue没有下标随机访问等操作,只有普通的pop_front,push_back,pop_back()等操作,而这些函数在其他容器中完全可以有,栈和队列的实现完全可以将其他容器的操作进行复用,这就是stack和queue作为容器适配器的原因。

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

一、deque

 1、deque 的原理介绍

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

 2、deque 的底层结构

  deque 的底层结构其实并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际 deque 类似于一个动态的二维数组,其结构示意图如下:

 3、deque 的迭代器

  双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落 在了deque的迭代器身上,因此 deque 的迭代器设计就比较复杂,如下图所示:

 4、deque 的优缺点

  4.1、优点

  • 具有 vector 的优点 ---支持随机访问、缓存命中率较高、尾部插入删除数据效率高;
  • 同时具有 list 的优点 --- 空间浪费少、头部插入插入数据效率高;

  4.2、缺点

  • deque 的随机访问效率较低 --- 需要先通过中控数据找到对应的buffer数组,再找到具体的位置 (假设偏移量为 i,需先 i/10 得到位于第几个buffer数组,再 i%10 得到 buffer 数组中的具体位置),即 deque 随机访问时一次跳过一个buffer数组,需要跳多次才能准确定位,其效率比 list 高了不少,但比 vector 也低了不少;
  • deque 在中部插入删除数据的效率是比较低的 --- 需要挪动数据,但不一定后续 buffe 数组中的数据全部挪动,可以控制只挪一部分,即中间插入删除数据的效率高于 vector,但是低于 list。

 所以综上分析, deque 结合了 vector 和 list 的优缺点,看似很完美,但是它单方面的性能是不如 vector 或者 list 的,因此 deque 在实际应用中使用的非常少。

STL 中选择 deque 作为 stack 和 queue 默认适配容器的原因:

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

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

  deque 特别适合需要大量进行头插和尾部数据的插入删除、偶尔随机访问、偶尔中部插入删除的场景;不太适合需要大量进行随机访问与中部数据插入删除的场景,特别是排序。

二、stack 的介绍和使用

 1、stack 的介绍

  1. stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。
  2. stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定 的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。
  3. stack 的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下操作:empty:判空操作 back:获取尾部元素操作 push_back:尾部插入元素操作 pop_back:尾部删除元素操作;
  4. 标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器, 默认情况下使用deque。

 2、stack 的使用

函数说明接口说明
stack()构造空的栈
empty()检测 stack 是否为空
size()返回 stack 中元素的个数
top()返回栈顶元素的引用
push()将元素 val 压入 stack 中
pop()将 stack 中尾部的元素弹出

下面是栈的使用操作: 

int main()
{
	//构造空栈
	stack<int> s;

	//元素入栈
	s.push(1);
	s.push(2);

	//获取栈中元素个数
	int Size = s.size();
	cout << Size << endl;

	//获取栈顶元素的引用
	int sTop = s.top();
	cout << sTop << endl;

	//元素出栈
	s.pop();
	sTop = s.top();
	cout << sTop << endl;

	//判断栈是否为空
	cout << s.empty();

	return 0;
}

 3、stack 的模拟实现

  我们在这里为栈模板定义了两个模板参数:是栈中存储的元素的类型Container 是栈模板使用的底层结构Container 的默认值是 vector,如果你想要用别的,可以在这里进行设置。我就可以将适配器作为类的第二个模板参数,然后通过传递不同的适配容器来实现栈了:

//stack.h
template<class T, class Container>
class stack 
{
	//...
};
//test.cpp
void test_stack() 
{
	stack<int, vector<int>> st1;
	stack<int, list<int>> st2;
}

vector 和 list 都可以作为 stack 的适配容器,我们可以通过给定不同的第二个模板参数来使用不同的容器适配 stack;

 经过前期的学习,显然更适合作为 stack 的适配容器,那么我们可以还可以将 vector 设置为 stack 的默认适配容器:

//stack.h
template<class T, class Container = vector<T>>
class stack 
{
	//...
};
//test.cpp
void test_stack() 
{
	//默认使用vector做适配容器
	stack<int> st1;  
	//使用其他容器做适配容器需要显式指定
	stack<int, list<int>> st2;  
}

 有了适配容器之后,我们就可以更容易的通过调用适配容器的接口来实现 stack 的接口了。

namespace xx
{
	//适配器模式/配接器
	template <class T, class Container = deque<T>>
	class stack
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_back();
		}
		const T& top()
		{
			return _con.back();
		}
		bool size()
		{
			return _con.size();
		}
		bool empty()
		{
			return _con.empty();
		}
	private:
		Container _con;
	};
	void test_stack()
	{
		//stack<int,vector<int>> st;//数组栈
		stack<int, list<int>> st;//链表栈
		st.push(1);
		st.push(2);
		st.push(3);
		st.push(4);
		while (!st.empty())
		{
			cout << st.top() << " ";
			st.pop();
		}
		cout << endl;
	}
}

stack 可以使用 vector 或者 list 来实现,效率相当。插入数据就相当于尾插,删除栈顶元素就相当于尾删。

三、queue 的介绍和使用

 1、queue 的介绍 

  1. 队列是一种容器适配器,专门用于在 FIFO 上下文(先进先出)中操作,其中从容器一端插入元素,另一端提取元素。
  2. 队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue 提供一组特定的 成员函数来访问其元素。元素从队尾入队列,从队头出队列。
  3. 底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作: empty:检测队列是否为空 ; size:返回队列中有效元素的个数; front:返回队头元素的引用;back:返回队尾元素的引用;push_back:在队列尾部入队列;pop_front:在队列头部出队列;
  4. 标准容器类 deque 和 list 满足了这些要求。默认情况下,如果没有为 queue 实例化指定容器类,则使用标准容器 deque。

 2、queue 的使用

函数声明接口说明
queue()构造空的队列
empty()检测队列是否为空,是返回 true,否则返回 false
size()返回队列中有效元素的个数
front()返回队头元素的引用
back()返回队尾元素的引用
push()在队尾将元素 val 入队列
pop()将队头元素出队列

下面是队列的使用操作:

int main() 
{
	//构造空队列
	queue<int> q;

	//元素入队
	q.push(1);
	q.push(2);

	//返回有效元素个数
	int size = q.size();
	cout << size << endl;

	//检查队列是否为空
	cout << q.empty() << endl;

	//获取队头元素的引用
	int front = q.front();
	cout << front << endl;

	//获取队尾元素的引用 
	int back = q.back();
	cout << back << endl;

	//队头元素出队
	q.pop();
	return 0;
}

 3、queue 的模拟实现

  它的模拟实现过程和 stack 类似,vector 和 list 都可以作为 queue 的适配容器,但是由于 queue 需要大量在头部删除数据,所以使用 deque 作为 queue 的默认适配容器,那么 queue 模拟实现的代码如下:

namespace xx
{
	template <class T, class Container = deque<T>>
	class queue
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_front();
		}
		const T& front()
		{
			return _con.front();
		}
		const T& back()
		{
			return _con.back();
		}
		bool size()
		{
			return _con.size();
		}
		bool empty()
		{
			return _con.empty();
		}
	private:
		Container _con;
	};
	void test_queue()
	{
		queue<int> q;
		q.push(1);
		q.push(2);
		q.push(3);
		q.push(4);
		while (!q.empty())
		{
			cout << q.front() << " ";
			q.pop();
		}
		cout << endl;
	}
}


本文要是有不足的地方,欢迎大家在下面评论,我会在第一时间更正。

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

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

相关文章

​专业图像处理软件 Photoshop 2023 mac版本更新(ps2023中文)

​Photoshop 2023 mac是一款图像编辑和图形设计软件&#xff0c;广泛应用于专业人士和爱好者。它提供了许多工具和功能&#xff0c;用于创建、编辑和增强数字图像&#xff0c;包括图层、蒙版、滤镜和各种选择工具。Photoshop还支持多种文件格式&#xff0c;包括psD、JPEG、PNG和…

APP开发者如何运用积分墙广告,提升APP应用下载和用户留存?

“积分墙”移动广告通过在应用内展示各种积分任务&#xff0c;鼓励用户完成任务以获得积分奖励&#xff0c;从而增加应用的曝光度和下载量。 一、什么是积分墙&#xff1f; 积分墙是一种第三方移动广告平台。开发者可以在这类平台上发布任务&#xff08;如下载安装 App、注册…

JavaScript - canvas - 放大镜

效果 示例 项目结构&#xff1a; 源码&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8" /><title>放大镜</title><style type"text/css">div {width: 200px;height: 200px;display: inline-bl…

Java集成支付宝沙箱支付,详细教程(SpringBoot完整版)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、开发前准备&#xff1f;二、使用步骤1、引入库2、配置在 application.yml 里面进行配置&#xff1a;3、alipay的java配置&#xff1a;AplipayConfig.java4、支付…

不要再滥用可选链运算符(?.)啦!

可选链运算符(?.)&#xff0c;大家都很熟悉了&#xff0c;直接看个例子&#xff1a; const result obj?.a?.b?.c?.d 很简单例子&#xff0c;上面代码?前面的属性如果是空值&#xff08;null或undefined&#xff09;&#xff0c;则result值是undefined&#xff0c;反之…

企业架构LNMP学习笔记58

开始学习Tomcat&#xff1a; 学习目标和内容&#xff1a; 1&#xff09;能够描述Tomcat的使用场景&#xff1b; 2&#xff09;能够简单描述Tomcat的工作原理&#xff1b; 3&#xff09;能够实现部署安装Tomcat&#xff1b; 4&#xff09;能够实现和配置Tomcat的Server服务…

如何判断自己的服务器是不是中了勒索病毒,勒索病毒解密数据恢复

勒索病毒日益横行&#xff0c;给企业的生产经营和个人数据造成了极大的影响。但是对单一的企业和个人而言&#xff0c;遭受勒索病毒攻击毕竟是低频率事件&#xff0c;这也导致很多人中招之后&#xff0c;无法判断出具体的问题&#xff0c;进而没有及时采取应对措施&#xff0c;…

【谢希尔 计算机网络】第3章 数据链路层

数据链路层 数据链路层的地位 网络中的主机、路由器等都必须实现数据链路层局域网中的主机、交换机等都必须实现数据链路层不同链路层可能采用不同的数据链路层协议 数据链路层信道类型 点对点信道 使用一对一的点对点通信方式广播通信 必须使用专用的共享系电脑协议来协调这些…

【Java 基础篇】Java多线程实现文件上传详解

文件上传是Web应用程序中常见的功能之一&#xff0c;用户可以通过网页将文件从本地计算机上传到服务器。在处理大文件或多用户并发上传的情况下&#xff0c;为了提高性能和用户体验&#xff0c;常常使用多线程来实现文件上传功能。本文将详细介绍如何使用Java多线程实现文件上传…

【RabbitMQ实战】docker 安装RabbitMQ(bitnami)

一、搜索镜像 [rootlocalhost ~]# docker search rabbitmq NAME DESCRIPTION STARS OFFICIAL AUTOMATED rabbitmq RabbitMQ is an open source multi-pro…

携手走过四年,极智嘉(Geek+)赋能上海西门子开关智慧物流升级

日前&#xff0c;上海西门子开关有限公司迎来了工厂30周年庆典暨客户开放日活动&#xff0c;全球仓储机器人引领者极智嘉(Geek)荣幸作为优秀供应商代表受邀参会&#xff0c;见证其突破革新、数智飞跃的三十载辉煌历程。 客户开放日活动中&#xff0c;上海西门子开关有限公司开…

【SpringCloud】微服务技术栈入门2 - Nacos框架与Feign

目录 Nacos下载 Nacos 并运行配置 NacosNacos 集群Nacos 负载均衡Nacos 环境隔离Nacos 注册细节Nacos 更多配置项快速上手自动更新 Feign取代 RestTemplateFeign 自定义配置性能优化 Nacos 下载 Nacos 并运行 首先下载对应的 release 包&#xff0c;主要要选择已经打包编译好…

链表的回文判断

思路: 找中间节点–>逆置->比较 代码&#xff1a; /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/struct ListNode* middleNode(struct ListNode* head) { struct ListNode*slowhead; struct ListNode*f…

maven多模块依赖包程序包xxx不存在

背景 rpc-common 被 rpc-server、rpc-client依赖 项目地址 https://github.com/pjmike/springboot-rpc-demo mvn clean install 打包时报错 报错信息 程序包xxxx不存在 找不到符号 原因分析 原因还不清楚&#xff0c;网友们帮解答一下 解决 主pom.xml 添加 <packaging…

23个销量最高的3D扫描仪【2023】

如果你可以 3D 扫描它&#xff0c;你就可以 3D 打印它。 市场上 3D 扫描仪的种类和质量非常丰富&#xff0c;机器尺寸、功能和价格各异。 这样的选择虽然本身是一件很棒的事情&#xff0c;但也会让从无用的东西中挑选出宝石成为一件苦差事。 推荐&#xff1a;用 NSDT编辑器 快速…

如何在linux定时备份opengauss数据库(linux核心至少在GLIBC_2.34及以上)

前提环境&#xff0c;linux的核心至少在GLIBC_2.34及以上才能使用。 查看linux的glibc版本的命令如下 strings /lib64/libc.so.6 | grep GLIBC 如下图 或者用ldd --version 如下图 在官网下载对应的依赖包&#xff0c; 只需要这个lib文件即可&#xff0c;将这个包放在lin…

拓世法宝|短视频带货风潮,数字人教育书单号成销售黑马

Z世代的爸妈&#xff0c;正在搞一种很新的育儿方式。 “躺平式”带娃、“用魔法打败魔法”等新时代育儿方式频频登上热搜&#xff0c;作为与互联网共同成长起来的一代&#xff0c;Z世代父母们更热衷于通过网络攻略获得和分享知识和经验&#xff0c;更注重个性的养育方式&#…

ChatGPT所引用的参考文献根本不存在?如何正确使用AI工具?

从ChatGPT推出以来&#xff0c;一直都是一个热点话题&#xff0c;尤其是在高等教育领域&#xff0c;大家展开了非常激烈的讨论。 巴斯大学对ChatGPT进行测试时发现&#xff0c;ChatGPT生成的论文&#xff0c;其参考文献的引用格式看起来很完美&#xff0c;有作者姓名和期刊名称…

文件、预处理、位运算

10.2 数据文件概述 10.2.1 ASCII文件与二进制文件 ASCII文件就是“将需要保存到文件的信息使用ASCII字符表示&#xff0c;然后按照顺序将每个字符的ASCII码存储到文件中”。ASCII文件的优点是编码方式公开&#xff0c;可以被其它的文本编辑器打开&#xff1b;其缺点是效率比较…

第一章 计算机网络基础

目录 1.1 网络体系结构 1.1.1 OSI/RM七层参考模型 1.1.2 OSI/RM和TCP/IP模型的比较 1.1.3 五层协议的体系结构 1.1.4 计算机1向计算机2发送数据过程 1.1.5 TCP/IP体系结构的具体实现 1.2 网络设备概述 1.2.1 互联设备与OSI的对应关系 1.2.2 集线器(HUB) 1.2.3 网桥(B…