【C++】容器适配器stack、queue以及deque容器

news2024/9/28 9:28:33

🏖️作者:@malloc不出对象
⛺专栏:C++的学习之路
👦个人简介:一名双非本科院校大二在读的科班编程菜鸟,努力编程只为赶上各位大佬的步伐🙈🙈
在这里插入图片描述

目录

    • 前言
    • 一、什么是容器适配器
      • 1.1 stack的介绍
      • 1.2 stack的使用
      • 1.3 queue的介绍
      • 1.4 queue的使用
    • 二、stack的模拟实现
    • 三、queue的模拟实现
    • 四、deque的简单介绍
      • 4.1 deque的原理介绍
      • 4.2 deque的缺陷
      • 4.3 性能测试
      • 4.3 为什么选择 deque 作为 stack 和 queue 的底层默认容器
      • 4.4 STL标准库中对于stack和queue的模拟实现


前言

本篇文章我们主要讲解的是C++中的容器适配器(stack、queue)以及它们的模拟实现!!还提到了容器deque和它的优缺点!!

一、什么是容器适配器

容器适配器是STL(标准模板库)中的一种特殊容器,它们通过在现有的容器之上提供新的接口和功能来改变现有容器的行为,可以帮助简化某些特定类型的操作。
容器适配器提供了许多不同的功能,包括栈(stack)、队列(queue)、优先队列(priority_queue)等。它们都是基于其他STL容器(如vector、deque、list)实现的,因此可以使用这些容器提供的底层数据结构来支持它们的操作。
总之,容器适配器是STL中的一种重要组件,它们提供了一种简单易用的方式来实现特定数据结构的操作,从而提高了编程效率和代码的可读性。同时,对于一些需要高效数据结构的场景,应该根据实际需求选择最适合的数据结构。

1.1 stack的介绍

关于stack想必不用我过多的进行介绍了吧,其中它最大的特点就是后进先出,在我们很多的设计场景中经常出现!!

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

1.2 stack的使用

函数说明接口说明
empty()判断栈是否为空
size()返回栈中元素的个数
top()返回栈顶元素的引用
push()将元素压入栈中
pop()将栈顶元素弹出

栈的使用成本很低,下面我们来简单的使用演示一下:

在这里插入图片描述

1.3 queue的介绍

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

1.4 queue的使用

函数说明接口说明
empty()判断队列是否为空
size()返回队列中元素的个数
front()返回队头元素的引用
back()返回队尾元素的引用
push()在队尾压入元素
pop()将队头元素出队列

我们简单的看下queue的使用:

在这里插入图片描述

二、stack的模拟实现

stack作为容器适配器,它可以通过使用 vector、list 和 deque等底层容器来实现它的函数接口,stl中的 stack和queue都是默认使用双端队列 deque来进行封装的,后续我们会谈及deque双端队列,下面我们默认使用的vector容器进行包装。

// stack.h
namespace curry
{
	template<class T, class Container = vector<T>>
	class stack
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}

		void pop()
		{
			_con.pop_back();
		}

		const T& top()
		{
			return _con.back();
		}

		size_t size()
		{
			return _con.size();
		}

		bool empty()
		{
			return _con.empty();
		}

	private:
		Container _con;
	};

	void test_stack()
	{
		stack<int> st;
		st.push(1);
		st.push(2);
		st.push(3);
		st.push(4);
		cout << st.size() << endl;

		while (!st.empty())
		{
			cout << st.top() << " ";
			st.pop();
		}
		cout << endl;
	}
}

在这里插入图片描述

三、queue的模拟实现

由于queue队列支持头插与头删,而我们的vector容器是不支持头插头删的,因为这样会大量挪动数据影响效率,所以这里我们采用list容器对它进行包装。

// queue.h
namespace curry
{
	template<class T, class Container = list<T>>
	class queue
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}

		// 队头出
		void pop()
		{
			_con.pop_front();
		}

		const T& back()
		{
			return _con.back();
		}

		const T& front()
		{
			return _con.front();
		}

		size_t 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);

		cout << q.size() << endl;
		cout << q.back() << endl;

		while (!q.empty())
		{
			cout << q.front() << " ";
			q.pop();
		}
		cout << endl;
	}
}

在这里插入图片描述

四、deque的简单介绍

4.1 deque的原理介绍

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

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

在这里插入图片描述

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

在这里插入图片描述

那deque是如何借助其迭代器维护其假想连续的结构呢?

在这里插入图片描述

下面我们简单的来看看deque的使用:

在这里插入图片描述

那么既然deque同时拥有了vector与list的性能,为何deque没有取代它们呢?

答案很显然,deque并没有想象中的那么强大,因为它是有很大缺陷的。

4.2 deque的缺陷

与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。
与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。

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

4.3 性能测试

#include <deque>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

// N个数据需要排序,vector+ 算法sort  deque+ sort
void Test()
{
	srand(time(0));
	const int N = 1000000;
	vector<int> v;
	v.reserve(N);

	deque<int> dq;
	for (int i = 0; i < N; ++i)
	{
		auto e = rand();
		v.push_back(e);
		dq.push_back(e);
	}

	int begin1 = clock();
	sort(v.begin(), v.end());
	int end1 = clock();

	int begin2 = clock();
	sort(dq.begin(), dq.end());
	int end2 = clock();

	printf("vector sort:%d\n", end1 - begin1);
	printf("dequeue sort:%d\n", end2 - begin2);
}

int main()
{
	Test();

	return 0;
}

在这里插入图片描述

在release版本下vector随机访问的速度大概是deque的两倍,而debug下deque的随机访问的速度比vector要慢上3倍多,可见deque随机访问的速率不如vector极致!!这是因为deque设计的机制导致随机访问的速率更慢。

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的优点,而完美的避开了其缺陷。

4.4 STL标准库中对于stack和queue的模拟实现

stack的模拟实现

在这里插入图片描述

namespace curry
{
	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();
		}

		size_t size()
		{
			return _con.size();
		}

		bool empty()
		{
			return _con.empty();
		}

	private:
		Container _con;
	};
}

在这里插入图片描述

queue的模拟实现:

在这里插入图片描述

namespace curry
{
	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& back()
		{
			return _con.back();
		}

		const T& front()
		{
			return _con.front();
		}

		size_t size()
		{
			return _con.size();
		}

		bool empty()
		{
			return _con.empty();
		}

	private:
		Container _con;
	};
}

在这里插入图片描述


本篇文章的内容就到这里了,如果文章有任何疑问或者错处欢迎大家评论区相互交流orz~🙈🙈

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

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

相关文章

开源软件的漏洞响应:应对安全威胁

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

岛屿数量00

题目链接 岛屿数量 题目描述 注意点 grid[i][j] 的值为 ‘0’ 或 ‘1’ 解答思路 使用广度优先遍历思想遍历整个岛屿遍历整个二维网络&#xff0c;如果此时位置处的值为1&#xff0c;则当前位置是一个岛的一部分&#xff0c;从该位置向着四个方向遍历出整个岛屿&#xff0…

【滑动窗口】leetcode3:无重复字符的最长子串

一.题目描述 无重复字符的最长子串 二.思路分析 题目要求我们找符合要求的最长子串&#xff0c;要求是不能包含重复字符 确定一个子串只需确定它的左右区间即可&#xff0c;于是我们可以两层循环暴力枚举所有的子串&#xff0c;找到符合要求的&#xff0c;并通过比较得到最长…

5G+智慧交通行业解决方案[46页PPT]

导读&#xff1a;原文《5G智慧交通行业解决方案[46页PPT]》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 喜欢文章&#xff0c;您可以点赞评论转发本文&#xff0c;…

基于Java+SpringBoot+Vue前后端分离贸易行业crm系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

小研究 - JVM 逃逸技术与 JRE 漏洞挖掘研究(七)

Java语言是最为流行的面向对象编程语言之一&#xff0c; Java运行时环境&#xff08;JRE&#xff09;拥有着非常大的用户群&#xff0c;其安全问题十分重要。近年来&#xff0c;由JRE漏洞引发的JVM逃逸攻击事件不断增多&#xff0c;对个人计算机安全造成了极大的威胁。研究JRE安…

小研究 - Java虚拟机内存管理分析

讨论了&#xff2a;&#xff41;&#xff56;&#xff41;关键技术组成&#xff0c;深入介绍了&#xff2a;&#xff41;&#xff56;&#xff41;虚拟机的体系结构&#xff0c;分析了虚拟机中内存管理的垃圾回收机制。同时&#xff0c;对现有的一些流行垃圾回收算法进行了深入…

5年经验之谈 —— APP和WEB的测试区别

在功能测试时&#xff0c;要考虑手机应用的特性&#xff1a; 1&#xff09;手机屏幕尺寸偏小&#xff0c;所以手机应用一般就占满了全屏&#xff0c;因此要考虑手机在前后端切换时被测试应用在资源使用时的优先级变化情况&#xff1b;还要考虑手机横竖屏切换时的测试 2&#xf…

yolov5中添加ShuffleAttention注意力机制

ShuffleAttention注意力机制简介 关于ShuffleAttention注意力机制的原理这里不再详细解释.论文参考如下链接here   yolov5中添加注意力机制 注意力机制分为接收通道数和不接受通道数两种。这次属于接受通道数注意力机制,这种注意力机制由于有通道数要求,所示我们添加的时候…

引导滤波(guided filter)与快速引导滤波(fast guided filter)理解

最近在学习图片的滤波和去噪的相关知识&#xff0c;查阅了一些资料参考了一些博客&#xff0c;这里做一个整合&#xff0b;理解。参考的博客资料在文末。 引入普通滤波的概念 假设输入图像为p&#xff0c;滤波窗口为wk,经过滤波后的输出图像为q,那么q图的第i个像素是由输入图p中…

Vue2向Vue3过度核心技术声明式导航

目录 1 声明式导航-导航链接1.需求2.解决方案3.通过router-link自带的两个样式进行高亮4.总结 2 声明式导航-两个类名1.router-link-active2.router-link-exact-active3.在地址栏中输入二级路由查看类名的添加4.总结 3 声明式导航-自定义类名1.问题2.解决方案3.代码演示4.总结 …

OpenSIPS 通话中 UPDATE 请求导致没有声音问题

文章目录 1. 问题现象2. 抓包排查3. 问题分析及解决方案 1. 问题现象 在 SIP 应用的开发中&#xff0c;通话一端听不到声音是比较常见的问题。一般来说&#xff0c;没有声音意味着 RTP 传输存在障碍&#xff0c;追根究底就是网络不通或者端口未开放等原因。但在实践中&#xf…

射频滤波器分析报告(声表面波滤波器/BAW/超声)

目录 一、射频芯片技术与产品概述二、5G时代滤波器需求潜力巨大三、全球滤波器市场现状3.1 基站3.2 手机端 四、射频芯片国内发展情况4.1 国内射频芯片概况4.2 国内射频滤波器发展情况4.3 BAW的重重困难4.4 终端厂商的参与 五 机会分析5.1 5G通信5.2 卫星通信5.3 雷达行业5.4 新…

Cinema 4D软件安装包分享(附安装教程)

目录 一、软件简介 二、软件下载 一、软件简介 Cinema 4D&#xff08;简称C4D&#xff09;是由德国Maxon Computer公司开发的一款三维动画渲染和建模软件&#xff0c;广泛应用于影视、广告、工业设计等领域。C4D因其高效率、易用性和强大的功能而受到广大设计师和艺术家的青睐…

第四章文件管理

0.初识文件管理 一个文件有哪些属性?文件名:由创建文件的用户决定文件名&#xff0c;主要是为了方便用户找到文件&#xff0c;同一目录下不允许有重名文件。 标识符:一个系统内的各文件标识符唯一&#xff0c;对用户来说毫无可读性,因此标识符只是操作系统用于区分各个文件的一…

pandas-03-组合连接数据

采集的数据存储后通常会分为多个文件或数据库&#xff0c;如何将这些文件按需拼接&#xff0c;或按键进行连接十分重要。这节将介绍数据索引的复杂操作如分层索引&#xff0c;stack,unstack,seet_index,reset_index等帮助重构数据&#xff0c;数据的拼接如merge,join,concat,co…

pandas数据分析——groupby得到分组后的数据

groupbyagg分组聚合对数据字段进行合并拼接 Pandas怎样实现groupby聚合后字符串列的合并&#xff08;四十&#xff09; groupby得到分组后的数据 pandas—groupby如何得到分组里的数据 date_range补齐缺失日期 在处理时间序列的数据中&#xff0c;有时候会遇到有些日期的数…

python编程环境使用技巧3-程序打包pyinstaller

前言 在Python中&#xff0c;打包指的是将Python代码和相关资源&#xff08;如配置文件、图像等&#xff09;整合到一个可执行的文件或安装包中&#xff0c;以便于在其他环境中使用。 下面是使用pyinstaller进行打包的简要步骤&#xff1a; 1-安装pyinstaller&#xff1a;在命…

python之OCR文字识别

将图片翻译成文字一般被称为光学文字识别&#xff08;Optical Character Recognition&#xff0c;OCR&#xff09;。可以实现OCR 的底层库并不多&#xff0c;目前很多库都是使用共同的几个底层OCR 库&#xff0c;或者是在上面进行定制。 方法一&#xff1a; 使用easyocr模块 …

Redis三种特殊数据类型

Redis三种特殊数据类型 geospatial 地理位置 Redis 地理空间数据类型简介 Redis 地理空间索引允许您存储坐标并搜索它们。 此数据结构可用于查找给定半径或边界框内的邻近点。 基本命令 GEOADD 将位置添加到给定的地理空间索引&#xff08;请注意&#xff0c;使用此命令&a…