C++学习之路,从0到精通的征途:stack_queue的模拟实现及deque原理介绍

目录

一.容器适配器

1.什么是适配器

2.STL标准库中stack和queue的底层结构

3.deque的原理介绍

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

头插

尾插

遍历

4.deque的优缺点

二.stack的模拟实现

三.queue的模拟实现


一.容器适配器

1.什么是适配器

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

2.STL标准库中stack和queue的底层结构

虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配器,这是因为stack和队列只是对其他容器的接口进行了包装,STL中stack和queue默认使用deque,比如:

3.deque的原理介绍

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

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

图中map为中控数组 ,来控制一维数组buffer 中的数据,当中控数组满载时,会进行realloc扩容。

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

迭代器中有4个成员变量,first指向当前buffer数组的头部last指向当前buffer数组尾部cur指向当前buffer数组中访问到的结点node为二级指针,指向中控数组中指向buffer数组的指针。

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

deque的start(iterator)用来控制中控数组中所指向的第一个buffer,finish(iterator) 用来控制中控数组中所指向的最后一个buffer。

头插

对于deque的头插,如果当前first所指向的buffer数组的头部还有空间,则直接在cur所指向的位置插入,如果当前first所指向的buffer数组的头部没有空间,那么在中控数组中重新申请一个buffer,更新first,再在cur位置进行插入操作:

尾插

对于deque的尾插,如果当前finish所指向的buffer数组的尾部还有空间,则直接在cur所指向的位置插入,如果当前finish所指向的buffer数组的尾部没有空间,那么在中控数组中重新申请一个buffer,更新finish,再在cur位置进行插入操作:

遍历

对于deque的遍历,可以通过begin()与end()中cur的比较 来进行,当begin()中的cur与end()中的cur相等时,说明遍历已完成,而cur在向后遍历的过程中,与list中的迭代器类似,在当前连续空间走完后,需要更新cur进入下一个buffer继续遍历,所以我们这里要将迭代器的++进行重载,这里进行一个简单的实现:

4.deque的优缺点

优点:

1.头尾插入删除效率很高,适合做stack和queue的默认适配容器。

2.下标随机访问效率也不错,与vector相比差一点
缺点:

1.中间位置插入删除效率一般

2.对比vector和list没有那么极致

二.stack的模拟实现

cpp 复制代码
namespace my_stack
{
	// 只要支持push_back,pop_back等接口函数接可以作为stack的容器适配器
	// 将适配器的尾部作为栈的开口方向,从而实现先进后出
	template<class T, class Container = deque<T>>
	class stack
	{
	public:
		stack()
		{}

		void push(const T& x)
		{
			_con.push_back(x);
		}

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

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

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

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

		bool empty() const
		{
			return size() == 0;
		}
	private:
		Container _con;
	};
}

三.queue的模拟实现

cpp 复制代码
namespace my_queue
{
	// 将容器适配器的头部作为队头,将尾部作为队尾,从而实现先进先出
	template<class T, class Container = deque<T>>
	class queue
	{
	public:
		queue()
		{}

		void push(const T& x)
		{
			_con.push_back(x);
		}

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

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

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

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

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

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

		bool empty()
		{
			return size() == 0;
		}
	private:
		Container _con;
	};
}
相关推荐
爱吃大芒果12 小时前
Flutter for OpenHarmony 实战:mango_shop 路由系统的配置与页面跳转逻辑
开发语言·javascript·flutter
学***542312 小时前
如何轻松避免网络负载过大
开发语言·网络·php
AI视觉网奇12 小时前
FBX AnimSequence] 动画长度13与导入帧率30 fps(子帧0.94)不兼容。动画必须与帧边界对齐。
笔记·学习·ue5
RANCE_atttackkk12 小时前
Springboot+langchain4j的RAG检索增强生成
java·开发语言·spring boot·后端·spring·ai·ai编程
梵刹古音12 小时前
【C语言】 格式控制符与输入输出函数
c语言·开发语言·嵌入式
Acrelhuang12 小时前
工商业用电成本高?安科瑞液冷储能一体机一站式解供能难题-安科瑞黄安南
大数据·开发语言·人工智能·物联网·安全
hello 早上好12 小时前
03_JVM(Java Virtual Machine)的生命周期
java·开发语言·jvm
沐雪架构师12 小时前
LangChain 1.0 Agent开发实战指南
开发语言·javascript·langchain
tod11312 小时前
力扣高频 SQL 50 题阶段总结(四)
开发语言·数据库·sql·算法·leetcode
科技林总12 小时前
使用Miniconda安装Jupyter
笔记