C++:stack queue - 容器适配器

C++:容器适配器

    • 容器适配器概念
    • stack
    • queue
    • deque


容器适配器概念

容器适配器是在C++标准库中提供的一种容器的封装。它们提供了一种统一的接口,使得不同类型的容器可以以相似的方式被使用。容器适配器有三种类型:栈(stack)、队列(queue)和优先队列(priority_queue)。其中优先队列其实就是数据结构中的堆(heap)。

我们看到这三种数据结构有一个共同的特点,那就是它们的规则是基于数据的,而不是基于内存的。比如说顺序表(vector)要求内存必须是连续的,链表(list)要求一个一个节点的形式来存储数据。它们都对内存有明确的限制。而以上三种数据结构,只是对数据的出入顺序有要求,所以它们的底层可以是vectorlist等等其它容器。

也就是说,它们可以将原本的容器进行封装,改变容器的出入规则,但是底层的数据存储依然使用其它的容器。这种模式叫做容器适配器。

接下来带大家实现stackqueue,帮助大家理解这种容器适配器模式。


stack

栈(stack)是一种先进后出(Last-In-First-Out,LIFO)的数据结构,它的特点是只能在栈的一端进行插入和删除操作。在栈中,插入元素的一端称为栈顶(top),删除元素的一端称为栈底(bottom)。栈的插入操作叫做入栈(push),删除操作叫做出栈(pop)。

栈的实现通常有两种方式:数组实现和链表实现。使用数组实现的栈叫做顺序栈,使用链表实现的栈叫做链式栈。
在此处,我们用vector作为底层容器,实现一个顺序栈。
在这里插入图片描述
以上就是STL库中stack的所有接口,我们选择里面最重点实现。

先看到基本结构:

template <class T>
class stack
{
private:vector<T> _con;
};

stack类内部,有一个成员变量_con,其类型为vector<T>,这就是我们stack的底层容器,后续我们stack进行操作,本质上都是对vector进行操作。

那么我们的vector哪一边做栈顶好呢?
如果我们用vector的头部做栈顶,那么每次入栈,所有数据都要往后移动一位;每次出栈,所有数据都要往前移动一位。
这会带来大量的时间浪费。
但是用vector的尾部做栈顶,那么每次入栈出栈,都不会影响其它数据,所以最好用尾部做栈顶。

入栈:
入栈其实就是对vector尾插:

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

出栈:
出栈就是对vector尾删:

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

返回栈顶元素:
返回栈顶,其实就是得到vector尾部的元素:

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

返回栈的大小:
栈的大小,就是vector的大小:

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

判断栈是否为空:
栈为空,其实就是vector为空:

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

可以发现,我们将底层设为vector后,无需再考虑底层是如何插入删除,只需要将我们配套的规则对应的接口用上去即可,这就是容器适配器带来的优势。
但是至此,还不算完整的容器适配器设计模式。如果用户想用list做底层,难道我们又要写一个list版本吗?
这是不需要的,我们不如直接传入第二个模板参数,让用户可以自定义需要的底层容器:

template <class T, class Container = vector<T>>
class stack
{
private:Container _con;
};

第二个模板参数container就是我们的底层容器,其默认值为vector,当用户不传入第二个参数,那么默认以vector为底层容器。

当用户需要list版本的stack

stack<int, list<int>> st;

这样我们就可以让用户想用什么底层容器,就用什么底层容器。

完整代码:

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

队列(Queue)是一种先进先出(First-In-First-Out,FIFO)的数据结构。在队列中,元素只能在一端(队尾)添加,而在另一端(队头)删除。新的元素只能添加到队尾,而只能从队头删除元素。
在这里插入图片描述
同样的我们来实现一下queue:

相比于vectorlist更适合作为queue的底层容器,因为queue需要头部删除,而vector头部删除的代价很大,要移动整个vector的数据,而list不需要,所以queue适合用list作为底层。

有了前面的铺垫,这里我就不额外讲解每个接口了:

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& front(){return _con.front();}const T& back(){return _con.back();}size_t size(){return _con.size();}bool empty(){return _con.empty();}private:Container _con;
};

前面的两个容器适配器,一个用list做底层,一个用vector做底层。但是在STL中,这两个容器适配器的默认底层容器都是一个叫做deque的容器,接下来我为大家介绍这个容器。


deque

deque是双端队列(double-ended queue)的缩写,它是一种具有特殊特性的线性数据结构。deque允许从两端进行元素的插入和删除操作。

我们看一下deque的结构:
在这里插入图片描述
deque分为两个区域,左下角的区域称为中控,其用于控制所有小数组,中控本质上是一个指针数组,内部的每个指针都指向了一个小数组。而我们是在右侧小数组中插入删除数据的。

deque的插入并不是从第一个位置开始插入,而是从中间开始插入
比如我们要插入数据“12345”
在这里插入图片描述
这么做的好处就是:适合头插尾插。
接下来我们再在头部插入数据“678910”

在这里插入图片描述

可以看到,我们进行头部插入,不会影响其他数据,这是它相对于vector的优势。而由于其内存是部分连续的,可以通过中控数组的指针偏移量与小数组的指针偏移量来锁定元素。所以其也可以支持下标随机访问。

deque的优势:

  1. 头尾插入删除效率很高
  2. 支持下标随机访问

那么它这么好用,为什么不直接替代vector和list呢?
那么我再讲一讲它的致命缺点:极度不适合中间插入
我们看到,这个结构中,数据被分为了很多个小段,如果我们在中间插入删除数据,就会导致数据的挪动很麻烦,因为会发生不同数组之间的数据迁移。所以其中间插入的效率非常非常低。
这也就是为什么它称为双端队列的原因,就是只适合两端的插入删除,

stackqueue两种数据结构,刚好都是不会发生中间的插入删除的,所以它们的默认底层容器是deque

最后总结一下:
deque的特性包括:

  1. 双端高效插入和删除操作:deque允许在队列的头部和尾部进行插入和删除操作。这意味着可以在队列的任意一端进行元素的添加和移除,而不仅限于一侧。插入和删除的时间复杂度是O(1),即常数时间。这使得deque在需要高效地在两端进行插入和删除操作的场景下非常有用。

  2. 随机访问:和数组类似,deque也支持随机访问。即通过下标访问第i个元素的时间复杂度是O(1)。这是因为在数组实现中,deque使用了连续的存储空间,并且通过指针可以直接定位到指定的元素。

  3. 空间效率:deque的空间效率较高。在数组实现中,由于使用了连续的存储空间,没有额外的指针和链表结构,因此空间占用较小。

总结起来,deque是一种具有特殊特性的线性数据结构,它兼具了队列和栈的特点,并且在两端插入和删除操作非常高效,同时也支持随机访问。这使得deque在需要频繁在两端进行插入和删除操作的场景中非常有用。但是不适合中间的插入删除。


容器适配器使得程序员可以不必直接处理底层容器的细节,而是使用统一的接口来操作不同类型的容器。这样,当需要改变底层容器时,只需要修改适配器的类型参数即可,而不需要修改大量的代码。容器适配器的使用可以提高代码的可维护性和可扩展性,同时也使代码更易于理解。


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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

springboot742餐厅点餐系统

springboot742餐厅点餐系统 获取源码——》公主号&#xff1a;计算机专业毕设大全

山东交警将全力做好恶劣天气交通秩序管控疏导工作,确保春运畅通

2月5日&#xff0c;山东省人民政府新闻办公室召开新闻发布会&#xff0c;针对春节期间人流、车流急剧增长态势以及不良天气的影响&#xff0c;公安交管部门将全力做好恶劣天气交通秩序管控疏导工作。 省公安厅交通警察总队总队长刘大为介绍&#xff0c;2024年春运各类交通出行需…

C++进阶(十六)特殊类设计

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、请设计一个类&#xff0c;不能被拷贝二、请设计一个类&#xff0c;只能在堆上创建对象三、…

HarmonyOS 通过getInspectorByKey获取指定元素高宽等属性

例如 这里 我们有这样一个组件 Entry Component struct Dom {build() {Column() {Row() {Circle({ width: 200, height: 200 }).fill(#20101010)}.id(ES)}.width(100%).height(100%)} }这里 我们就写了个很基本的组件结构 然后 我们写了个 Circle 组件 定义了宽高 然后 如果我…

第四篇【传奇开心果微博系列】Python微项目技术点案例示例:美女颜值判官

传奇开心果微博系列 系列微博目录Python微项目技术点案例示例系列 微博目录一、微项目目标二、雏形示例代码三、扩展思路四、添加不同类型的美女示例代码五、增加难度等级示例代码六、添加特殊道具示例代码七、设计关卡系统示例代码八、添加音效和背景音乐示例代码九、多人游戏…

黑马程序员微信小程序学习总结7.工具脚本、自定义组件、data和properties的区别

目录 工具脚本&#xff08;utils中的wxs&#xff09;自定义组件组件的引用方式局部引用全局引用组件和页面的区别组件样式隔离data数据组件method事件处理函数自定义组件添加属性 data和properties的区别使用setData修改properties的值 工具脚本&#xff08;utils中的wxs&#…

有依赖的背包问题——树形DP+分组背包

有 N 个物品和一个容量是 V 的背包。 物品之间具有依赖关系&#xff0c;且依赖关系组成一棵树的形状。如果选择一个物品&#xff0c;则必须选择它的父节点。 如下图所示&#xff1a; 如果选择物品5&#xff0c;则必须选择物品1和2。这是因为2是5的父节点&#xff0c;1是2的父节…

MySQL基本操作之数据库的操作

一.创建数据库 1.基本语法 create database 数据库名&#xff1b; 注意别忘记加分号。 2.if not exists 数据库名字是唯一的&#xff0c;所以不可以创建已存在的数据库&#xff0c;如下&#xff1a; 重复创建就会报错 所以有了if not exists这个语法&#xff0c;加上之后&…

nvm 安装nodejs教程【详细】

目录 一、安装nvm 二、配置镜像 三、安装nodejs 安装 查看正在用的nodejs版本 切换版本 一、安装nvm 双击安装包&#xff1a; 无脑下一步即可&#xff0c;当然你可以自定义你自己的安装目录。 安装完后&#xff0c;打开环境变量&#xff0c;你会发现nvm为我们自动配置好…

ubuntu22.04@laptop OpenCV Get Started: 008_image_filtering_using_convolution

ubuntu22.04laptop OpenCV Get Started: 008_image_filtering_using_convolution 1. 源由2. convolution应用Demo2.1 C应用Demo2.2 Python应用Demo 3. 重点分析3.1 identity矩阵3.2 all ones 5x5矩阵3.3 blur 5x5矩阵3.4 GaussianBlur 5x5矩阵3.5 medianBlur 5x5矩阵3.6 Sharpe…

TinUI v5预发布记录

TinUI v5预发布记录 前言新控件滚动选择框菜单按钮 新样式pre1pre2pre3pre4 新功能导入字体文件释放子窗口 前言 TinUI是一个从2021年正式开始并一直维护到现在的小项目&#xff0c;中间经过了四代版本的更新。因为一些原因&#xff0c;2023年&#xff0c;TinUI-4后更新较少。…

勒索病毒最新变种.target勒索病毒来袭,如何恢复受感染的数据?

导言&#xff1a; 在当今数字化时代&#xff0c;数据被视为企业和个人最重要的资产之一。然而&#xff0c;随着技术的进步&#xff0c;网络安全威胁也在不断演变。其中&#xff0c;勒索病毒是一种极具破坏性的威胁&#xff0c;而".target"勒索病毒是近期备受关注的一…