C++中stack和queue的模拟实现

目录

1.容器适配器  

1.1什么是适配器      

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

1.3deque的简单介绍

1.3.1deque的原理介绍 

1.3.2deque的优点和缺陷

1.3.3deque和vector进行排序的性能对比

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

2.stack的介绍和模拟实现

2.1stack的介绍

2.2stack的模拟实现 

2.2.1传统栈的结构

2.2.2利用vector作为stack的底层容器 

 2.2.3模板中加一个容器类型参数,实现不同容器作为stack的底层容器

3.queue的介绍和模拟实现 

3.1queue的介绍

3.2queue的模拟实现 

3.2.1传统队列的结构

3.2.2利用list作为queue的底层容器 

3.2.3模板中加一个容器类型参数,实现不同容器作为queue的底层容器 


1.容器适配器  

1.1什么是适配器      

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

        如上图,需要将两个插口的插头转换为插座能使用的三个插口的插头,就需要一个适配器进行转换,适配器也可以叫做转换器。

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

        虽然stack和queue中也可以存放元素,但是STL中并没有将其划分在容器的行列,而是将其称为容器适配器,这里因为stack和queue只是对其他容器的接口进行了包装,所以stack和queue相当于上述三个抽口的插头,底层容器相当于两个插口的插头,用户需要一个stack或queue的结构,就调用其他的底层容器来弄出一个stack或queue,比如:

1.3deque的简单介绍

1.3.1deque的原理介绍 

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

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

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

        如上图,deque的存储空间放在一个中控器(可以理解为内存中开辟的一块用于存储deque里面数据的空间)里面,里面的每一方框就是一个缓冲器(这里的缓冲区相当于二维数组的每一行)。上述表明deque的迭代器中有4个东西:(1)node:表示指向的是哪一个缓冲区(相当于表面指向二维数组里面的哪一行)。(2)cur:表面在这个缓冲区的位置。(3)first和last:记录该缓冲区的起始位置和结束位置。具体怎么进行维护的这里就不过多的进行介绍了。

1.3.2deque的优点和缺陷

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

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

1.3.3deque和vector进行排序的性能对比

        这里在vs2022的Release下对vector和deque进行排序的性能比较,第一个是分别在各自容器中进行排序之后时间消耗的比较,第二个是将deque中的数据copy到vector进行排序后返回到deque中与deque中自己排序的时间消耗进行比较。

#include <iostream>
#include <vector>
#include <list>
#include <stack>
#include <queue>
#include <algorithm>
using namespace std;

void test_op1()
{
	srand(time(0));
	const int N = 1000000;

	deque<int> dq;
	vector<int> v;

	for (int i = 0; i < N; i++)
	{
		auto e = rand() + i;
		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();

	cout << "vector: " << end1 - begin1 << endl;
	cout << "deque:  " << end2 - begin2 << endl;
}

void test_op2()
{
	srand(time(0));
	const int N = 1000000;

	deque<int> dq1;
	deque<int> dq2;

	for (int i = 0; i < N; ++i)
	{
		auto e = rand() + i;
		dq1.push_back(e);
		dq2.push_back(e);
	}

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

	int begin2 = clock();
	// 拷贝到vector
	vector<int> v(dq2.begin(), dq2.end());
	sort(v.begin(), v.end());
	dq2.assign(v.begin(), v.end());
	int end2 = clock();

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

int main()
{
	test_op1();
	test_op2();
	return 0;
}

1.4为什么选择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不仅效率高,而且内存使用率高。

2.stack的介绍和模拟实现

2.1stack的介绍

2.2stack的模拟实现 

2.2.1传统栈的结构

template <class T>
class Stack
{
private:
	T* _a;    //用数组存储元素
	size_t _top;
	size_t _capacity;
};

2.2.2利用vector作为stack的底层容器 

        这里在stack类里面有一个vector的对象,所有的stack接口通过调用vector的接口进行实现。

namespace XiaoC
{
	template<class T>
	class stack
	{
	public:
		//会调用vector的构造函数进行构造
		stack()
		{}

		void push(const T& x)
		{
			_c.push_back(x);	//调用vector的push_back接口
		}

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

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

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

		bool empty() const
		{
			return _c.empty();
		}
	private:
		std::vector<T> _c;
	};
}

 2.2.3模板中加一个容器类型参数,实现不同容器作为stack的底层容器

        这里默认给的容器是deque,对于stack来说只要支持push_back()和pop_back()线性结构容器就能用来当作底层容器。

#pragma once
#include <deque>
namespace XiaoC
{
	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() const
		{
			return _con.back();
		}

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

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

	private:
		Container _con;
	};
}

3.queue的介绍和模拟实现 

3.1queue的介绍

3.2queue的模拟实现 

3.2.1传统队列的结构

// 链式结构:表示队列中每个节点的结构
typedef struct QListNode
{ 
    struct QListNode* _pNext; 
    QDataType _data; 
}QNode;
 
// 队列的结构,用两个指针来维护一个队列
typedef struct Queue
{ 
    QNode* _front; 
    QNode* _rear; 
}Queue;

3.2.2利用list作为queue的底层容器 

         这里queue里面有一个list对象,所有queue的接口通过调用list的接口进行实现。

#pragma once
#include <deque>

namespace XiaoC
{
	template<class T>
	class queue
	{
	public:
		queue()
		{}

		void empty()
		{
			return _c.empty();
		}

		void size()
		{
			return _c.size();
		}

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

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

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

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

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

		const T& back() const
		{
			return _c.back();
		}
	private:
		std::list<T> _c;
	};
}

3.2.3模板中加一个容器类型参数,实现不同容器作为queue的底层容器 

        这里默认给的容器是deque,对于queue来说只要支持push_back()和pop_front()线性结构容器就能用来当作底层容器。

namespace XiaoC
{
	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() const
		{
			return _con.front();
		}

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

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

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

	private:
		Container _con;
	};
}

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

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

相关文章

MybatisPlus代码生成器的使用

在使用MybatisPlus以后&#xff0c;基础的Mapper、Service、PO代码相对固定&#xff0c;重复编写也比较麻烦。因此MybatisPlus官方提供了代码生成器根据数据库表结构生成PO、Mapper、Service等相关代码。只不过代码生成器同样要编码使用&#xff0c;也很麻烦。 这里推荐大家使…

【ios】---swift开发从入门到放弃

swift开发从入门到放弃 环境swift入门变量与常量类型安全和类型推断print函数字符串整数双精度布尔运算符数组集合set字典区间元祖可选类型循环语句条件语句switch语句函数枚举类型闭包数组方法结构体 环境 1.在App Store下载Xcode 2.新建项目&#xff08;可以先使用这个&…

JSR303微服务校验

一.创建idea 二.向pom.xml添加依赖 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.7.RELEASE</version></parent><properties><java.vers…

SpringCloud-基于Docker和Docker-Compose的项目部署

一、初始化环境 1. 卸载旧版本 首先&#xff0c;卸载可能已存在的旧版本 Docker。如果您不确定是否安装过&#xff0c;可以直接执行以下命令&#xff1a; sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logro…

数字化那点事:一文读懂数字孪生

一、数字孪生的定义 数字孪生&#xff08;Digital Twin&#xff09;是指通过数字技术构建的物理实体的虚拟模型&#xff0c;能够对该实体进行全方位、动态跟踪和仿真预测。简单来说&#xff0c;数字孪生就是在一个设备或系统的基础上创造一个数字版的“克隆体”&#xff0c;这…

【RADARSAT Constellation Mission(RCM)卫星星座简介】

RADARSAT Constellation Mission&#xff08;RCM&#xff09;卫星星座是加拿大太空局&#xff08;CSA&#xff09;的下一代C波段合成孔径雷达&#xff08;SAR&#xff09;卫星星座&#xff0c;以下是对其的详细介绍&#xff1a; 一、基本信息 发射时间&#xff1a;2019年6月…

在Linux系统安装Nginx

注意&#xff1a;Nginx端口号是80(云服务器要放行) 我的是基于yum源安装 安装yum源(下面这4步就好了) YUM源 1、将源文件备份 cd /etc/yum.repos.d/ && mkdir backup && mv *repo backup/ 2、下载阿里源文件 curl -o /etc/yum.repos.d/CentOS-Base.repo ht…

Adobe PR与AE的区别与联系(附网盘地址)

从事视频后期制作的小伙伴&#xff0c;对于PR&#xff08;Premiere&#xff09;和AE&#xff08;After Effects&#xff09;应该不会陌生。随着短视频的兴起&#xff0c;就连我们普通用户&#xff0c;拍摄完视频&#xff0c;都会去糟取精的剪辑一下&#xff0c;而PR正是一款功能…

【分布式微服务云原生】windows+docker+mysql5.7.44一主一从主从复制

目录 1. 主库设置2. 从库设置3. 验证主从复制内容汇总表格 摘要&#xff1a; 在Windows系统上通过Docker部署MySQL主从复制&#xff0c;以下是详细的步骤和命令&#xff0c;帮助你设置一主一从的MySQL复制环境。 1. 主库设置 步骤1&#xff1a;运行MySQL主库容器 docker run …

探索Python网络世界的利器:Requests-HTML库

文章目录 探索Python网络世界的利器&#xff1a;Requests-HTML库背景&#xff1a;为何选择Requests-HTML&#xff1f;什么是Requests-HTML&#xff1f;如何安装Requests-HTML&#xff1f;5个简单库函数的使用方法3个场景下库的使用示例常见Bug及解决方案总结 探索Python网络世界…

微服务nginx解析部署使用全流程

目录 1、nginx介绍 1、简介 2、反向代理 3、负载均衡 2、安装nginx 1、下载nginx 2、解压nginx安装包 3、安装nginx​编辑 1、执行configure命令 2、执行make命令 4、启动nginx 1、查找nginx位置并启动 2、常用命令 3、反向代理 1、介绍反向代理配置 1、基础配置…

【iOS】计算器的仿写

计算器 文章目录 计算器前言简单的四则运算UI界面事件的逻辑小结 前言 笔者应组内要求&#xff0c;简单实现了一个可以完成简单四则运算的计算器程序。UI界面则是通过最近学习的Masonry库来实现的&#xff0c;而简单的四则运算内容则是通过栈来实现一个简单的四则运算。 简单…

Halcon实用系列1-识别二维条码

在做项目时&#xff0c;之前使用的是某康的智能读码器&#xff0c;综合考虑成本&#xff0c;可通过相机拍照来读取图片的二维码&#xff0c;我这边用Halcon来实现。 Halcon代码如下&#xff1a; *创建模型 create_data_code_2d_model(Data Matrix ECC 200, [], [], DataCodeH…

5G NR物理信道简介

文章目录 NR 上行物理信道PRACHPUCCHPUSCH NR 下行物理信道PBCHPDCCHPDSCH NR 上行物理信道 PRACH PRACH&#xff08;Physical Random Access Channel)物理随机接入信道&#xff0c;用于传导preamble 序列。PRACH 由循环前缀CP、前导序列和保护间隔三部分组成。 PUCCH PUCCH…

【AI大模型】深入Transformer架构:编码器部分的实现与解析(上)

目录 &#x1f354; 编码器介绍 &#x1f354; 掩码张量 2.1 掩码张量介绍 2.2 掩码张量的作用 2.3 生成掩码张量的代码分析 2.4 掩码张量的可视化 2.5 掩码张量总结 &#x1f354; 注意力机制 3.1 注意力计算规则的代码分析 3.2 带有mask的输入参数&#xff1a; 3.…

FOCShield v2.0.4原理图

1.FOCShield v2.0.4原理图,开源原文件用AD制作。用 AD09可以打开。 主要部分为 1.电机驱动芯片部分 2.电流采样部分

Windows 环境下安装 Anaconda 并适配到 PowerShell 的保姆级教程

Anaconda Anaconda 是一个流行的 Python 数据科学和机器学习平台&#xff0c;它包括了 Conda 包管理器、Python 以及数百个用于科学计算的库和工具。Anaconda 旨在简化包和环境管理&#xff0c;使得安装、更新和管理软件包变得容易&#xff0c;同时也能够轻松创建和切换不同的P…

大数据毕业设计选题推荐-民族服饰数据分析系统-Python数据可视化-Hive-Hadoop-Spark

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

【Linux】命令管道

一、命名管道的介绍 之前的管道博客中介绍的是匿名管道&#xff0c;这个管道的应用的一个限制就是只能在具有公共祖先&#xff08;具有亲缘关系&#xff09;的进程间通信。 如果我们不想在不相关的进程之间交换数据&#xff0c;可以使用FIFO文件来做这项工作&#xff0c;他经常…

输入捕获模式测频率PWMI模式测频率占空比

前沿知识&#xff1a;TIM输入捕获-CSDN博客 输入捕获相关函数 // 初始化输入捕获单元 // ICInit是4个通道共用一个函数的&#xff0c;第二个结构体参数&#xff0c;可以用来配置具体是哪个通道。 void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);/…