C++容器适配器stack和queue(含deque,priority_queue)

news/2024/10/30 23:14:28/

目录

1.容器适配器

  1.1 什么是适配器

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

  1.3 deque

        1.3.1 deque原理介绍(了解)

        1.3.2 deque优点和缺点

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

2. stack介绍和使用

  2.1 stack介绍

  2.2 stack使用

  2.3 stack模拟实现

3. queue介绍和使用

  3.1 queue的介绍

  3.2 queue的使用

  3.3 queue模拟实现

4. priority_queue

  4.1 priority_queue的介绍

  4.2 priority_queue的使用

  4.3 priority_queue模拟实现


1.容器适配器

  1.1 什么是适配器

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

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

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

  

  1.3 deque

        1.3.1 deque原理介绍(了解)

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

deque 并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际 deque 类似于一个动态的二维 数组 ,其底层结构如下图所示:
双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其 整体连续 以及随机访问的假象,落 在了 deque 的迭代器身上, 因此 deque 的迭代器设计就比较复杂,如下图所示:

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

        1.3.2 deque优点和缺点

 

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

deque:1.operator[ ]计算稍显复杂,大量使用,性能下降(相比vector)

              2.中间插入删除效率不高

              3. 底层角度迭代器会很复杂

        1.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 的优点,而完美的避开了其缺陷。
结论:

1.头尾插入删除非常适合,相比vector和List而言,很适合去做stack和queue的默认适配容器

2.中间插入删除多用list

3.随机访问多用vector

2. stack介绍和使用

  2.1 stack介绍

  • stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。
  • stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。
  • stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下操作:

        empty:判空操作

        size:  返回栈中有效元素的个数

        back:获取尾部元素操作

        push_back:尾部插入元素操作

        pop_back:尾部删除元素操作

  • 标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器,默认情况下使用deque。

  2.2 stack使用

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

将stack中尾部的元素弹出

 

 

  2.3 stack模拟实现

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

3. queue介绍和使用

  3.1 queue的介绍

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

  3.2 queue的使用

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

 

 

 

 

 

 

 

 

 

  3.3 queue模拟实现

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

4. priority_queue

  4.1 priority_queue的介绍

仿函数(Functor)又称为函数对象(Function Object)是一个能行使函数功能的类。

仿函数的语法几乎和我们普通的函数调用一样,不过作为仿函数的类,都必须重载 operator() 运算符。因为调用仿函数,实际上就是通过类对象调用重载后的 operator() 运算符。

  • 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
  • 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
  • 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
  • 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:
        empty():检测容器是否为空
        size():返回容器中有效元素个数
        front():返回容器中第一个元素的引用
        push_back():在容器尾部插入元素
        pop_back():删除容器尾部元素
  • 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector。
  • 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数 make_heap、push_heap和pop_heap来自动完成此操作。

  4.2 priority_queue的使用

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。注意:默认情况下priority_queue是大堆。
函数声明接口说明

priority_queue()

priority_queue(first,last)

构造一个空的优先级队列
empty()检测优先级队列是否为空,是返回true,否则返回false
top()返回优先级队列中最大(最小元素),即堆顶元素
push()在优先级队列中插入元素val
pop()删除优先级队列中最大(最小)元素,即堆顶元素
大顶堆(降序)
//构造一个空的优先队列(此优先队列默认为大顶堆)
priority_queue<int> big_heap;   //另一种构建大顶堆的方法
priority_queue<int,vector<int>,less<int> > big_heap2;  

小顶堆(升序)

//构造一个空的优先队列,此优先队列是一个小顶堆
priority_queue<int,vector<int>,greater<int> > small_heap;  

注意:

如果使用less和greater,需要头文件:

#include <functional>

如果在priority_queue中放自定义类型的数据,用户需要在自定义类型中提供> 或者< 的重载。 

  4.3 priority_queue模拟实现

template<class T, class Container = vector<T>,class Compare=std::less<T>>class priority_queue{public:priority_queue(){}template<class InputIterator>priority_queue(InputIterator first, InputIterator last){while (first != last){_con.push_back(*first);++first;}//建堆,(_con.size()-1-1)/2为末尾节点的父节点所在位置for (int i = ((_con.size() - 1 - 1) / 2); i >= 0; i--){//向下建堆logNadjust_down(i);}}//向上建堆void adjust_up(size_t child){/*size_t parent = (child - 1) / 2;while (child > 0){//建大堆if (_con[parent] < _con[child]){std::swap(_con[parent], _con[child]);child = parent;parent = (child - 1) / 2;}else{break;}}*/Compare com;//仿函数,类对象像函数一样使用,重载operator()size_t parent = (child - 1) / 2;while (child > 0){//建大堆if (com(_con[parent],_con[child])){std::swap(_con[parent], _con[child]);child = parent;parent = (child - 1) / 2;}else{break;}}}void push(const T& x){//先vector插入,再向上调整_con.push_back(x);adjust_up(_con.size() - 1);}//向下建堆void adjust_down(size_t parent){/*size_t child = parent * 2 + 1;while (child < _con.size()){//选出左右孩子大的那一个if (child + 1 < _con.size() && _con[child] < _con[child + 1]){++child;}//建大堆if (_con[parent] < _con[child]){std::swap(_con[parent], _con[child]);parent = child;child = parent * 2 + 1;}else{break;}}*/Compare com;//仿函数,类对象像函数一样使用,重载operator()size_t child = parent * 2 + 1;while (child < _con.size()){//选出左右孩子大的那一个if (child + 1 < _con.size() && com(_con[child] , _con[child + 1])){++child;}//建大堆if (com(_con[parent] , _con[child])){std::swap(_con[parent], _con[child]);parent = child;child = parent * 2 + 1;}else{break;}}}void pop(){//根与最右下边的孩子交换,vector尾删,再下调整std::swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}const T& top() const{return _con[0];}bool empty() const{return _con.empty();}size_t size() const{return _con.size();}private:Container _con;};

这里就不赘述建堆过程,小伙伴可自行搜索数据结构内容复习。


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

相关文章

删除lpt1.css.asp或com8.index.asp这类文件的方法_asp木马无法删除解决办法

aux|prn|con|nul|com1|com2|com3|com4|com5|com6|com7|com8|com9|lpt1|lpt2|lpt3|lpt4|lpt5|lpt6|lpt7|lpt8|lpt9 但是可以通过cmd的copy命令实现&#xff1a; D:\wwwroot>copy rootkit.asp \\.\D:\wwwroot\lpt6.80sec.asp 前面必须有 \\.\ 已复制 1 个文件。 D…

K8S之自定义Controller

简介 在此之前我们先来了解下kubernetes的两个概念"声明式API"和"控制器模式"。"声明式API"核心原理就是当用户向kubernetes提交了一个API对象的描述后&#xff0c;Kubernetes会负责为你保证整个集群里各项资源的状态&#xff0c;都与你的API对象…

产品经理该怎么催进度?

这算是一个项目管理相关的问题&#xff0c;很多公司会把产品经理与项目经理的工作职能划分并没有这么清晰&#xff0c;而且项目是否能够按时上线&#xff0c;在整个项目推进过程中也是至关重要的。如果是公司的自研产品&#xff0c;项目没办法定期交付&#xff0c;挨老板一顿骂…

K8s基础3——应用部署流程、服务编排、集群资源利用率、日志管理

文章目录 一、应用部署流程二、服务编排2.1 YAML文件格式说明2.2 部署应用2.2.1 命令部署2.2.2 yaml文件部署2.2.2.1 编写deployment.yaml文件2.2.2.2 编写service.yaml文件2.2.2.3 两个yaml文件混用2.2.2.4 测试——service和deployment的标签不一致导致访问网页混乱 2.2.3 自…

腹部肿瘤内科专家朱利明:化疗也能“订制”,晚期结直肠癌不再“无药可救”

肠癌是发生在结肠和直肠的癌症&#xff0c;近二三十年来发病率快速上升。就在近期&#xff0c;“日本女大胃王菅原初代患肠癌病逝”的消息登上热搜&#xff0c;一时引发网友关注热议。 “人生有哲学三问&#xff1a;我是谁&#xff1f;我从哪里来&#xff1f;我到哪里去&#x…

初识区块链

初识区块链 01货币的发展 货币从古到今一直存在&#xff0c;并且不断地发展。 从古代的贝壳、铜钱&#xff0c;到现代的纸币、电子支付&#xff0c;货币的演变历程就像是人类文明的一部分。在古代&#xff0c;人们用物物交换来满足自己的需求&#xff0c;但随着社会的发展和…

IPWorks SSH 2022.0.8505 C++ Edition Crack

IPWorks SSH 2022.0.8505 C Edition 轻松将安全外壳 &#xff08;SSH&#xff09; 安全性集成到您的互联网应用程序中。IPWorks SSH 库包括支持 SSH 的客户端、服务器和代理组件&#xff0c;支持强 SSH 2.0 加密和高级加密。 SSH库 SSH 文件传输和通信 借助 IPWorks SSH&#x…

JVM 程序计数器(PC 寄存器)

PC Register 介绍 JVM中的程序计数寄存器( Program Counter Register) 中&#xff0c;Register 的命名源于 CPU 的寄存器, 寄存器存储指令相关的现场信息。 CPU 只有把数据装载到寄存器才能够运行JVM 中的 PC 寄存器是对物理 PC 寄存器的一种抽象模拟PC 寄存器用来存储指向下一…