【C++】详解STL容器之一的deque和适配器stack,queue

news/2024/10/22 5:05:26/

目录

deque%E7%9A%84%E6%A6%82%E8%BF%B0-toc" style="margin-left:0px;">deque的概述

deque%E7%A9%BA%E9%97%B4%E7%9A%84%E7%BB%93%E6%9E%84-toc" style="margin-left:0px;">deque空间的结构

deque%E7%9A%84%E8%BF%AD%E4%BB%A3%E5%99%A8-toc" style="margin-left:0px;">deque的迭代器

deque%E7%9A%84%E6%95%B0%E6%8D%AE%E8%AE%BE%E8%AE%A1-toc" style="margin-left:0px;">deque的数据设计

deque%E7%9A%84%E4%BC%98%E7%BC%BA%E7%82%B9-toc" style="margin-left:0px;">deque的优缺点

适配器的概念

​编辑

stack的概述

stack的模拟实现

queue的概述

queue的模拟实现


deque%E7%9A%84%E6%A6%82%E8%BF%B0" style="background-color:transparent;">deque的概述

deque的设计参考了另外两大容器vectorlist。可参考下面两篇文章

详解vector:http://t.csdnimg.cn/19TWM

详解list:http://t.csdnimg.cn/2rCbL

vector容器管理的是线性空间,vector容器是单向开口。这说明vector容器的头部插入,头部删除的时间效率是O(N),尾部插入,尾部删除的效率是O(1)。

与之相对的deque所管理的空间也可以看作是线性空间deque的线性空间是双向开口。这说明deque容器头部插入,头部删除,尾部插入,尾部删除的效率是O(1)

deque管理的空间不够时,不需要像vector那样成倍的扩容。此时,管理一段线性空间的deque却能像list的那样一小段一小段的扩容,并且还能保证自己管理的依旧是一段线性空间。

既然deque管理的是一段线性的空间,那一定支持随机访问,那么在deque容器中排序的效率怎么样呢?实际上,在deque容器中的排序效率并不理想,在一亿这个数据量级,用deque容器排序的用时是用vector容器排序用时的五倍。现在给出如下两个方案,方案一:直接用deque排序。方案二:把deque容器中的数据拷贝给vector,让vector排序,再把排好的数据拷贝给deque。方案一的效率也远远的不如方案二。

从排序的效率中可以看出,deque容器管理的线性空间是一种假象

deque%E7%A9%BA%E9%97%B4%E7%9A%84%E7%BB%93%E6%9E%84">deque空间的结构

deque会开一小段空间存数据,如果空间不够,会再开一小段相等大小的空间,这一小段空间称为缓冲区这些缓冲区的才是deque容器存数据的空间而这些一小段一小段的空间的指针会按一定顺序存入一段名为map的连续的空间,map就是管理所有缓冲区的中控器,如下图

如下是源码定义

template <class T, class Alloc = alloc, size_t BufSize = 0>
class deque
{
public:
typedef T value_type;
typedef value_type* pointer;//......
protected:
typedef pointer* map_pointer;  //指向数据的指针protected:
map_pointer map; //储存指针空间的map,本质是个二级指针,map指向的连续的空间size_t map_size; //map可容纳指针的个数
}

deque%E7%9A%84%E8%BF%AD%E4%BB%A3%E5%99%A8" style="background-color:transparent;">deque的迭代器

要让如此复杂的空间关系在上层表现的像线性空间一样,需要设计一个很复杂的迭代器。源码的迭代器封装了四个指针。

T* cur; 用来遍历缓冲区,也可以用来数据的存取

T* first; 指向缓冲区的头

T* last; 指向缓冲区的尾

map_pointer node; 指向中控器的指针,被指向的指针又指向该缓冲区

用如此复杂的迭代器想要随机访问数据是要付出代价的,因为缓冲区在内存中是不连续的,想要从一个缓冲区去下一个缓冲区,必须通过迭代器的node指针在中控器的空间上偏移实现。这便是用deque容器排序效率不高的原因。

deque%E7%9A%84%E6%95%B0%E6%8D%AE%E8%AE%BE%E8%AE%A1">deque的数据设计

iterator start; 用一个迭代器指向第一个缓冲区

iterator finish; 用一个迭代器指向最后一个缓冲区

map_pointer map; 指向中控器,方便管理中控器

size_type map_size; 中控器中有多少指针


deque%E7%9A%84%E4%BC%98%E7%BC%BA%E7%82%B9" style="background-color:transparent;">deque的优缺点

总结一下deque容器的优缺点

 deque优点: 

vector比较,头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不 需要搬移大量的元素,头插头删,尾插尾删的效率很高
list比较,其底层是连续空间,空间利用率比list较高,不需要存储额外字段。缓存命中率比list
deque缺点:
vector比较deque不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到
某段小空间的边界,导致效率低下,因此用deque容器排序的效率也是低下的

list比较:deque不适合在中间插入数据,因为需要挪动数据。而list只需改变指向关系即可。

vectorlist都有很明显的优点和很明显的缺点,deque就好像夹在vectorlist中间的容器,找不到很亮眼的优点。

适配器的概念

适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口。
虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配
器,这是因为stack和queue只是对其他容器的接口进行了包装,STL中stack和queue默认使用deque,比如:

STL在实现stack, queue时,并没有再去设计一个容器,而是直接复用其他容器,这便是适配器。这是一种设计思想


stack的概述

stack别名为,栈是一种先进后出的数据结构。栈只有一个开口,只能从这个开口入数据和出数据。

栈没有迭代器,这说明栈是不允许遍历的

栈的底层可以适配vector容器list容器deque容器默认适配deque容器

stack的模拟实现

#include<vector>
#include<list>
#include<deque>namespace bit
{// template<class T, class Container = deque<T>>class stack{public:void push(const T& x){_con.push_back(x);}void pop(){_con.pop_back();}T& top(){return _con.back();}size_t size(){return _con.size();}bool empty(){return _con.empty();}private:Container _con;};

queue的概述

dueue别名为队列,队列是一种先进先出的数据结构。队列只能队尾如数据,队头出数据。

队列没有迭代器,这说明队列是不允许遍历的

队列的底层可以适配list容器deque容器默认适配deque容器


queue的模拟实现

#include<vector>
#include<list>
#include<deque>namespace bit
{// template<class T, class Container = deque<T>>class queue{public:void push(const T& x){_con.push_back(x);}void pop(){_con.pop_front();//_con.erase(_con.begin());}T& front(){return _con.front();}T& back(){return _con.back();}size_t size(){return _con.size();}bool empty(){return _con.empty();}private:Container _con;};


http://www.ppmy.cn/news/1458861.html

相关文章

探索无界知识:用 ChatGPT 的原理学习任何事物!

为避免文章重复&#xff0c;您的文本已通过更改句式、用词以及句子结构进行了修改。现在的文本应该能更好地满足去重的需求&#xff1a; 从ChatGPT原理出发&#xff0c;我们探讨GPT如何启发人类学习和构建个人知识体系。 1. 明确学习目标 机器学习必须依靠目标函数。同样&…

《ESP8266通信指南》11-Lua开发环境配置

往期 《ESP8266通信指南》10-MQTT通信&#xff08;Arduino开发&#xff09;-CSDN博客 《ESP8266通信指南》9-TCP通信&#xff08;Arudino开发&#xff09;-CSDN博客 《ESP8266通信指南》8-连接WIFI&#xff08;Arduino开发&#xff09;&#xff08;非常简单&#xff09;-CSD…

初学者理解Transformer,本文is all you need

要问现在AI领域哪个概念最热&#xff0c;必然是openAI推出chatGPT之后引发的大模型。然而这项技术的起源&#xff0c;都来自一篇google公司员工的神作“Attention Is All You Need”——本文标题也是一种致敬^_^&#xff0c;目前已有近12万的引用(还在增长)。 在“Attention Is…

05.网络维护与管理命令

网络维护与管理命令 ifconfig 命令 功能说明 ifconfig 命令用来配置网络或显示当前网络接口状态。类似于 Windows下的ipconfig 命令&#xff0c;同时ifconfig命令必须以root用户来执行。其格式如下&#xff1a; ifconfig [选项] [interface] [inet|up|down|netmask|addr|broad…

Mybatis框架笔记:基础信息

1.Mybatis介绍 MyBatis本是apache的一个开源项目iBatis&#xff0c;2010年这个项目由apache software foundation迁移到了google code&#xff0c;并且改名为MyBatis。2013年11月迁移到Github。 iBATIS一词来源于“internet”和“abatis”的组合&#xff0c;是一个基于Java的持…

postman---认证(Certificates)是什么作用?

在 Postman 中&#xff0c;认证&#xff08;Certificates&#xff09;功能主要用于处理 TLS 客户端认证。TLS&#xff08;传输层安全性&#xff09;是用于保护网络通信安全的协议&#xff0c;它使用数字证书来验证通信双方的身份。在 Postman 中&#xff0c;认证功能允许您上传…

LifeCycle之ProcessLifeCycleOwner

问题&#xff1a;想要知道应用程序当前处在前台、后台、或从后台回到前台&#xff0c;想要知道应用的状态&#xff0c; LifeCycle提供了ProcessLifeCycleOwner的类&#xff0c;方便我们知道整个应用程序的生命周期情况 ProcessLifeCycleOwner 使用方法 1.首先添加依赖 imple…

MySql使用binlog日志恢复误删数据(强化)—— 筑梦之路

对于数据库误删数据恢复&#xff0c;推荐定期备份&#xff0c;通过定期备份的文件进行恢复&#xff0c;这里主要是使用binlog日志方式来恢复误删数据&#xff0c;之前也写过相关内容&#xff0c;这里再次强化一下步骤流程&#xff0c;增强熟悉程度。 概述 MySQL数据库可以开启…