c++之stack_queue与反向迭代器的实现

目录

1. 简单介绍stack与queue的使用

1.1 stack的介绍与使用

stack的介绍

stack的使用 

相关题目

1.2 queue的介绍与使用 

queue的介绍

queue的使用

相关题目

2.stack与queue的模拟实现 

容器适配器 

2.1 stack的模拟实现

2.2 queue的模拟实现 

2.3 priority_queue的模拟实现  

仿函数是什么? 

3. deque的介绍 

3.1 deque的原理 

3.2 deque的缺陷 

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

4. 反向迭代器 


1. 简单介绍stack与queue的使用

1.1 stack的介绍与使用

stack的介绍

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

 对于栈的特性想必大家都不陌生,下图是它的特性图。

stack的使用 

stack的使用很简单,最常用的就是下表的几个函数,无非压栈出栈,判空取尾。

函数说明接口说明
stack()构造空的栈
empty()检测stack是否为空
size()返回stack中元素的个数
top()返回栈顶元素的引用
push()将元素val压入stack中
pop()将stack中尾部的元素弹出

这里就不做示范了,非常简单。

相关题目

155. 最小栈 - 力扣(LeetCode)

栈的压入、弹出序列_牛客题霸_牛客网 (nowcoder.com)

150. 逆波兰表达式求值 - 力扣(LeetCode)

1.2 queue的介绍与使用 

queue的介绍

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

queue的使用

函数声明接口说明
queue()构造空的队列
empty()检测队列是否为空,是返回true,否则返回false
size()返回队列中有效元素的个数
front()返回队头元素的引用
back()返回队尾元素的引用
push()在队尾将元素val入队列
pop()将队头元素出队列

相关题目

225. 用队列实现栈 - 力扣(LeetCode)

2.stack与queue的模拟实现 

容器适配器 

学习stack与queue的模拟实现就必然要与容器适配器打招呼,那么什么是容器适配器呢? 

在stack的介绍中我们看到有这样一句话。 

下图有两个不同的容器,把水分别倒进去,此时两边的水就有了圆柱与圆锥的形状,但本质还是水,把沙子倒进去也一样。我们要学习的容器适配器同理。

分别用vector与list实现stack,此时的vector与list就具有了栈后进先出的特性,我们要实现的各种功能也都是调用vector与list的接口。

分别用vector与list实现queue,此时的vector与list就具有了队列先进先出的特性,我们要实现的各种功能同样都是调用vector与list的接口。

在这里可以把stack与queue理解成不同特性的容器。将vector等容器填充进去,使他们具备容器的特性。

接下来我们来看看c++中的适配器是什么?

在C++中,适配器(adapter)通常指的是一种设计模式,用于将一个类的接口转换成另一个类的接口,以便两者能够协同工作而无需修改原始类的代码。适配器模式可以分为类适配器模式和对象适配器模式。

1. 类适配器模式:通过继承原始类和实现目标接口来实现适配器。
2. 对象适配器模式:通过包含原始类的实例并实现目标接口来实现适配器。

在STL(标准模板库)中,也有一些称为适配器的特定类,如:
std::stack:基于deque、list或vector实现的栈适配器。
std::queue:基于deque或list实现的队列适配器。
std::priority_queue:基于vector实现的优先队列适配器。

这些适配器类提供了一些特定的接口和功能,使得使用栈、队列或优先队列更加方便和高效。

2.1 stack的模拟实现

既然已经明白了stack是一个容器适配器,那么stack的模拟实现自然就是向其内填充 vector等容器。

这里要注意,因为实现stack可以使用vector,list,deque等容器,因此我们需要使用到模板。

在模板参数中,我们将容器参数缺省为deque。各种功能接口也都是调用其内容器的接口。

	template <class T, class container = deque<T>>class stack{public:stack(container con = container()):_con(con){}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 _con.empty();}private:container _con;};

2.2 queue的模拟实现 

queue的原理同stack一样,直接上代码。

    template<class T, class Con = deque<T>>class queue{public:queue(Con con=Con()):_con(con){}void push(const T& x){_con.push_back(x);}void pop(){_con.pop_front();}T& back(){}const T& back()const{return _con.back();}T& front(){return _con.front();}const T& front()const{return _con.front();}size_t size()const{return _con.size();}bool empty()const{return _con.empty();}private:Con _con;};

2.3 priority_queue的模拟实现  

 priority_queue是什么呢?

优先级队列,顾名思义,优先级队列是会给队列中的数据排序的,那么是怎么做到的呢?

这里请想想堆,堆在插入数据时尾插,插入时按照向上调整算法维护堆;删除数据时删头,交换头尾,删除尾,再进行向下调整算法维护堆。和队列的特性非常符合,是完美的优先级队列底层结构。

template <class T>
class less
{
public:bool operator()(T x, T y){return x < y;}
};
template <class T>
class greater
{
public:bool operator()(T x, T y){return x > y;}
};
template <class T, class Container = vector<T>, class Compare = greater<T> >
class priority_queue{public:Compare com;priority_queue(Container con=Container()):_con(con){}bool empty() const{return _con.empty();}size_t size() const{return _con.size();}const T& top() const{return _con[0];}void Addjust_Up(int child){T parent = (child - 1) / 2;while (parent >= 0){if (com(_con[child], _con[parent])){std::swap(_con[parent], _con[child]);child = parent;parent = (child - 1) / 2;}else{break;}}}void push(const T& x){_con.push_back(x);Addjust_Up(_con.size()-1);}void AddJust_Down(int parent){size_t child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && com(_con[child + 1], _con[child])){++child;}if (com(_con[child], _con[parent])){std::swap(_con[parent], _con[child]);parent = child;child = parent * 2 + 1;}else{break;}}}void pop()//堆顶的删除,先交换堆顶与堆尾,然后删除堆尾,再向下调整{std::swap(_con[0], _con[_con.size() - 1]);_con.pop_back();AddJust_Down(0);}
private:Container _con;
};

priority_queue的模拟实现相较普通queue要复杂一些,因为我们要实现建堆与堆删除的向上调整与向下调整算法。同时堆有大堆小堆之分,但他们的代码只在某个位置有所不同,为了提高代码的复用率,我们使用了仿函数。

仿函数是什么? 

在C++中,仿函数(functor)是一个类或结构体,它重载了函数调用运算符(),从而可以像函数一样被调用。仿函数可以用作函数对象,用于实现自定义的函数行为。 

如下图就是仿函数的使用方法。 

在优先级队列的向上(下)调整算法中我们使用到了它。

3. deque的介绍 

在上文中,我们提到stack与deque的默认实现结构都是deque,那么他到底有怎样神奇的力量呢?

3.1 deque的原理 

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

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

这里的map是一个指针数组,map内存放的指针各自指向一个存放数据的数组。我们将map内存放的数组叫做buffer。


 

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

对于一个数组buffer,first指向buffer的第一个元素,last指向最后一个元素的下一个,cur指当前元素,node则是指向buffer的,用来表明当前处于哪一个buffer。 


 

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

不同的编译器,对于buffer的处理不同,每一个map内存放的缓冲区buffer有可能是登场的,也可能是非等长的,这里我们只需要了解。


3.2 deque的缺陷 

与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。
与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。
但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构。

3.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. 反向迭代器 

在之前的list与vector模拟实现中,我们实现了他们的正向迭代器,并未对反向迭代器做处理。

为什么在这里谈反向迭代器呢?是因为当我们实现了stack与queue后再学习反向迭代器会简单很多,反向迭代器的实现与stack有异曲同工之妙,让我们来看看吧!

下面的代码就是反向迭代器的模拟实现。复用传入的迭代器来实现我们需要的功能。

这里的rbegin与rend指向位置同begin,end位置是对称的。因此我们实现operator*时需要返回前一个的数值。

	template<class Iterator, class Ref, class Ptr>struct Reverse_iterator{typedef Reverse_iterator<Iterator, Ref, Ptr> self;Reverse_iterator(Iterator it){_it(it);}Ref operator*(){Iterator tmp(_it);tmp--;return *tmp;}Ptr operator->(){return &(operator*());}self& operator++(){return --_it;}self& operator++(int){Iterator tmp(_it);--_it;return tmp;}self& operator--(){return ++_it;}self& operator--(int){Iterator tmp(_it);++_it;return tmp;}bool operator==(const self& s){return s._it == _it;}bool operator!=(const self& s){return s._it != _it;}Iterator _it;};


 

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

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

相关文章

嵌入式STM32F407CET6移植OpenHarmony系统方法

第一:【实验目的】 1、STM32F407CET6开发版移植鸿蒙系统的方式 第二:【实验原理】 涉及到原理图添加原理图--普通STM32F407原理图第三:【实验步骤】 一、下载LiteOs源码,复制到到虚拟机中并解压 https://gitee.com/LiteOS/LiteOS

WebLogic-XMLDecoder(CVE-2017-10271)反序列化漏洞分析及复现

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收…

SS3D翻译

SS3D networka missingannotated instance mining module 缺失注释实例挖掘模块Score-based filteringIoU-guided suppressionFinal-step instance bank processing a reliable background mining module 可靠背景挖掘模块point cloud filling data augmentation 点云填充数据增…

(学习日记)2024.04.16:UCOSIII第四十四节:内存管理

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

HackTheBox-Machines--MonitorsTwo

文章目录 0x01 信息收集0x02 CVE-2022-46169 漏洞利用0x03 权限提升0x04 提升到root权限 MonitorsTwo 测试过程 0x01 信息收集 a.端口扫描: 发现22、80端口    b.信息收集: 1.2.22 Cacti信息收集 nmap -sC -sV 10.129.186.1321.访问 10.129.186.132&#xff0c;为 1.2.22 Ca…

底层文件操作的各种函数(二)------printf,fprintf,sprintf,scanf,fscanf,sscanf的对比以及文件缓冲区

偷得几日清闲&#xff0c;又因一瞬之间对蹉跎时间的愧疚&#xff0c;由此而来到CSDN这个高手云集和新手求学的平台来也写上那么一篇博客。虽然自己的博客那么久不温不热&#xff0c;但坚持写作&#xff0c;巩固自己就好。今天要讲的是续接上一篇文章的补充与继续吧。上期文章&a…

免费升级至HTTPS协议教程

一、前言 HTTPS协议以其安全性和数据加密特性&#xff0c;逐渐取代HTTP成为互联网通信的主流协议。本文将为您简洁明了地介绍如何免费升级至HTTPS协议。 二、获取免费SSL证书 选择证书提供商&#xff1a;如JoySSL等提供免费SSL证书的服务。 免费申请地址https://www.joyssl.…

太阳光光照试验耐久性老化试验使用太阳光模拟器系统

上海科迎法电气科技有限公司生产的太阳光模拟器系统主要应用于太阳能研究、材料研究、光伏组件测试、空间环境模拟器、植物生长研究、光热模拟等领域&#xff0c;主要表现特征为&#xff1a; 1. 太阳能研究&#xff1a;可用于模拟不同光照条件下太阳能电池的性能测试和研究&am…

Golang 开发实战day10 - Maps

&#x1f3c6;个人专栏 &#x1f93a; leetcode &#x1f9d7; Leetcode Prime &#x1f3c7; Golang20天教程 &#x1f6b4;‍♂️ Java问题收集园地 &#x1f334; 成长感悟 欢迎大家观看&#xff0c;不执着于追求顶峰&#xff0c;只享受探索过程 Golang 教程10 - Maps 1. M…

【计算机毕业设计】企业销售人员培训——后附源码

&#x1f389;**欢迎来到琛哥的技术世界&#xff01;**&#x1f389; &#x1f4d8; 博主小档案&#xff1a; 琛哥&#xff0c;一名来自世界500强的资深程序猿&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 琛哥在深度学习任务中展现出卓越的能力&a…

基于Python的深度学习的中文情感分析系统(V2.0),附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

Mysql主从复制安装配置

mysql主从复制安装配置 1、基础设置准备 #操作系统&#xff1a; centos6.5 #mysql版本&#xff1a; 5.7 #两台虚拟机&#xff1a; node1:192.168.85.111&#xff08;主&#xff09; node2:192.168.85.112&#xff08;从&#xff09;2、安装mysql数据库 #详细安装和卸载的步骤…