【c++】stack和queue使用 stack和queue模拟实现

主页:醋溜马桶圈-CSDN博客

专栏:c++_醋溜马桶圈的博客-CSDN博客

gitee:mnxcc (mnxcc) - Gitee.com

目录

1. stack的介绍和使用

1.1 stack的介绍

1.2 stack的使用

1.3 stack的模拟实现

2. queue的介绍和使用

2.1 queue的介绍

2.2 queue的使用

2.3 queue的模拟实现

3. priority_queue的介绍和使用

3.1 priority_queue的介绍

3.2 priority_queue的使用

3.3 priority_queue的模拟实现

4. 容器适配器

4.1 什么是适配器

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

4.3 deque的简单介绍(了解)

4.3.1 deque的原理介绍

4.3.2 deque的缺陷

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

4.5 STL标准库中对于stack和queue的模拟实现 

4.5.1 stack的模拟实现

4.5.2 queue的模拟实现

4.5.3 测试代码


 

1. stack的介绍和使用

1.1 stack的介绍

stack的文档介绍:https://cplusplus.com/reference/stack/stack/?kw=stack

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

1.2 stack的使用

1.3 stack的模拟实现

从栈的接口中可以看出,栈实际是一种特殊的vector,因此使用vector完全可以模拟实现stack

#include<vector>
namespace name
{template<class T>class stack{public:stack() {}void push(const T& x) { _c.push_back(x); }void pop() { _c.pop_back(); }T& top() { return _c.back(); }const 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. queue的介绍和使用

2.1 queue的介绍

https://cplusplus.com/reference/queue/queue/queue的文档介绍:https://cplusplus.com/reference/queue/queue/

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

2.2 queue的使用

2.3 queue的模拟实现

因为queue的接口中存在头删和尾插,因此使用vector来封装效率太低,故可以借助list来模拟实现queue,具体如下:

#include <list>
namespace name
{template<class T>class queue{public:queue() {}void push(const T& x) { _c.push_back(x); }void pop() { _c.pop_front(); }T& back() { return _c.back(); }const T& back()const { return _c.back(); }T& front() { return _c.front(); }const T& front()const { return _c.front(); }size_t size()const { return _c.size(); }bool empty()const { return _c.empty(); }private:std::list<T> _c;};
}

3. priority_queue的介绍和使用

3.1 priority_queue的介绍

priority_queue文档介绍:https://cplusplus.com/reference/queue/priority_queue/

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

3.2 priority_queue的使用

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue

注意:默认情况下priority_queue是大堆

  1. 默认情况下,priority_queue是大堆
    #include <vector>
    #include <queue>
    #include <functional> // greater算法的头文件
    void TestPriorityQueue()
    {// 默认情况下,创建的是大堆,其底层按照小于号比较vector<int> v{ 3,2,7,6,0,4,1,9,8,5 };priority_queue<int> q1;for (auto& e : v)q1.push(e);cout << q1.top() << endl;// 如果要创建小堆,将第三个模板参数换成greater比较方式priority_queue<int, vector<int>, greater<int>> q2(v.begin(), v.end());cout << q2.top() << endl;
    }
  2. 如果在priority_queue中放自定义类型的数据,用户需要在自定义类型中提供> 或者< 的重载
    class Date
    {
    public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}bool operator<(const Date& d)const{return (_year < d._year) ||(_year == d._year && _month < d._month) ||(_year == d._year && _month == d._month && _day < d._day);}bool operator>(const Date& d)const{return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}friend ostream& operator<<(ostream& _cout, const Date& d){_cout << d._year << "-" << d._month << "-" << d._day;return _cout;}
    private:int _year;int _month;int _day;
    };
    void TestPriorityQueue()
    {// 大堆,需要用户在自定义类型中提供<的重载priority_queue<Date> q1;q1.push(Date(2018, 10, 29));q1.push(Date(2018, 10, 28));q1.push(Date(2018, 10, 30));cout << q1.top() << endl;// 如果要创建小堆,需要用户提供>的重载priority_queue<Date, vector<Date>, greater<Date>> q2;q2.push(Date(2018, 10, 29));q2.push(Date(2018, 10, 28));q2.push(Date(2018, 10, 30));cout << q2.top() << endl;
    }

3.3 priority_queue的模拟实现

#pragma once#include <iostream>
using namespace std;#include <vector>
// priority_queue--->堆
namespace bite
{template<class T>struct less{bool operator()(const T& left, const T& right){return left < right;}};template<class T>struct greater{bool operator()(const T& left, const T& right){return left > right;}};template<class T, class Container = std::vector<T>, class Compare = less<T>>class priority_queue{public:// 创造空的优先级队列priority_queue() : c() {}template<class Iterator>priority_queue(Iterator first, Iterator last): c(first, last){// 将c中的元素调整成堆的结构int count = c.size();int root = ((count - 2) >> 1);for (; root >= 0; root--)AdjustDown(root);}void push(const T& data){c.push_back(data);AdjustUP(c.size() - 1);}void pop(){if (empty())return;swap(c.front(), c.back());c.pop_back();AdjustDown(0);}size_t size()const{return c.size();}bool empty()const{return c.empty();}// 堆顶元素不允许修改,因为:堆顶元素修改可以会破坏堆的特性const T& top()const{return c.front();}private:// 向上调整void AdjustUP(int child){int parent = ((child - 1) >> 1);while (child){if (Compare()(c[parent], c[child])){swap(c[child], c[parent]);child = parent;parent = ((child - 1) >> 1);}else{return;}}}// 向下调整void AdjustDown(int parent){size_t child = parent * 2 + 1;while (child < c.size()){// 找以parent为根的较大的孩子if (child + 1 < c.size() && Compare()(c[child], c[child + 1]))child += 1;// 检测双亲是否满足情况if (Compare()(c[parent], c[child])){swap(c[child], c[parent]);parent = child;child = parent * 2 + 1;}elsereturn;}}private:Container c;};
}void TestQueuePriority()
{bite::priority_queue<int> q1;q1.push(5);q1.push(1);q1.push(4);q1.push(2);q1.push(3);q1.push(6);cout << q1.top() << endl;q1.pop();q1.pop();cout << q1.top() << endl;vector<int> v{ 5,1,4,2,3,6 };bite::priority_queue<int, vector<int>, bite::greater<int>> q2(v.begin(), v.end());cout << q2.top() << endl;q2.pop();q2.pop();cout << q2.top() << endl;
}

4. 容器适配器

4.1 什么是适配器

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

4.2 STL标准库中stackqueue的底层结构

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

4.3 deque的简单介绍(了解)

4.3.1 deque的原理介绍

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

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

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

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

4.3.2 deque的缺陷

与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的

与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段

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

4.4 为什么选择deque作为stackqueue的底层默认容器

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.5 STL标准库中对于stackqueue的模拟实现 

4.5.1 stack的模拟实现

#pragma once
#include<vector>
#include<list>
#include<deque>
namespace dc
{//适配器模式//stack <int, vector<int>> st1;//stack <int,list<int>> st2;//template<class T,class Container=vector<int>>template<class T,class Container=deque<T>>class stack{public:void push(const T& x){_con.push_back(x);}void pop(){_con.pop_back();}size_t size(){return _con.size();}bool empty(){return _con.empty();}const T& top(){return _con.back();}private:Container _con;//T* _a;//int _top;//int _capacity;};
}

4.5.2 queue的模拟实现

#pragma once
#include<vector>
#include<list>
#include<deque>
namespace dc
{//适配器模式//stack <int, vector<int>> st1;//stack <int,list<int>> st2;template<class T, class Container = deque<T>>class queue{public:void push(const T& x){_con.push_back(x);}void pop(){_con.pop_front();}size_t size(){return _con.size();}bool empty(){return _con.empty();}const T& front(){return _con.front();}const T& back(){return _con.back();}private:Container _con;//T* _a;//int _top;//int _capacity;};
}

4.5.3 测试代码

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
using namespace std;
//#include<stack>
#include "Stack.h"
#include "Queue.h"
#include <algorithm>
void test_stack1()
{//dc::stack <int, vector<int>> st;dc::stack <int> st;st.push(1);st.push(2);st.push(3);st.push(4);while (!st.empty()){cout << st.top() << " ";st.pop();}cout << endl;
}
void test_queue1()
{//dc::stack <int, vector<int>> st;dc::queue <int> q;q.push(1);q.push(2);q.push(3);q.push(4);while (!q.empty()){cout << q.front() << " ";q.pop();}cout << endl;
}int main()
{//test_stack1();test_queue1();return 0;
}

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

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

相关文章

SpringBoot之JWT令牌校验

SpringBoot之JWT令牌校验 本文根据黑马b站springboot3vue3课程 JWT &#xff08;JSON Web Token&#xff09;是一种开放标准&#xff08;RFC 7519&#xff09;&#xff0c;用于在不同实体之间安全地传输信息。它由三个部分组成&#xff1a;头部&#xff08;Header&#xff09;…

如何实现音乐音频合并?分享3种简单的合并技巧!音频合并的方法

音乐合并&#xff0c;作为一种音乐创作与编辑的手法&#xff0c;已经逐渐在音乐制作领域占据了一席之地。音乐合并不仅是对音乐元素的重新组合&#xff0c;更是对音乐内涵的深化和拓展。它可以将不同的音乐风格和元素巧妙地融合在一起&#xff0c;创造出全新的听觉体验。 一&a…

DonkeyDocker-v1-0渗透思路

MY_BLOG https://xyaxxya.github.io/2024/04/13/DonkeyDocker-v1-0%E6%B8%97%E9%80%8F%E6%80%9D%E8%B7%AF/ date: 2024-04-13 19:15:10 tags: 内网渗透Dockerfile categories: 内网渗透vulnhub 靶机下载地址 https://www.vulnhub.com/entry/donkeydocker-1,189/ 靶机IP&a…

芯片设计围炉札记

文章目录 语言Verilog 和 VHDL 区别 芯片验证 语言 System Verilog的概念以及与verilog的对比 IC 设计软件分析 Verilog 和 VHDL 区别 Verilog HDL 和 VHDL 的区别如下&#xff1a; 语法结构&#xff1a;Verilog的语法结构类似于C语言&#xff0c;而VHDL的语法结构则更接近…

苍穹外卖学习记录(一)

1.JWT令牌认证 JSON Web Token (JWT)是一个开放标准(RFC 7519)&#xff0c;它定义了一种紧凑的、自包含的方式&#xff0c;用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任&#xff0c;因为它是数字签名的。 JWT是目前最常用的一种令牌规范&#xff0c;它最…

QT-编译报库错误(LF/CRLF)

QT-安装后环境问题记录 版本和环境问题 版本和环境 QT5.15.2 Windows10 QT Creator 问题 在QT夸端开发的项目中 &#xff0c;使用QTCreator打开项目pro文件&#xff0c;编译报出很多系统库 及本地文件中的一些问题&#xff0c;具体如图&#xff1a; 后续&#xff0c;我以为…

数字人结合动捕设备化身虚拟主持人如何玩转大型活动?

由十五运会和残特奥会广州赛区执委会、广州市政府新闻办、广州市科学技术局联合举办的“科技赋能 畅想全运”故事会上&#xff0c;数字人“小运”结合动捕设备化身虚拟主持人惊喜亮相&#xff0c;与真人主持人趣味互动&#xff0c;并向大众介绍了其后续将在大运会上给运动员、工…

redis的主从复制(docker方式快速入门和实战)

目录 一、主从复制简介 二、配置主从服务器 2.1使用配置文件的形式来主从复制 2.2使用纯代码的方式来进行主从复制&#xff1b; 2.3脱离主服务器 三、一些注意事项 一、主从复制简介 主从复制&#xff0c;是指将一台Redis服务器的数据&#xff0c;复制到其他的Redis服务器…

APEX开发过程中需要注意的小细节5.5

oracle保留小数点后两位的函数 在日常开发中经常用到百分比做数据对比&#xff0c;但是有可能得到的数据是一个多位小数&#xff0c;结果如下所示&#xff1a; 如果想截取部分小数如保留小数点后两位可以怎么做呢&#xff1f; 在Oracle中&#xff0c;可以使用ROUND函数来四舍…

51单片机入门_江协科技_31~32_OB记录的自学笔记_LCD1602液晶显示屏

31. LCD1602 31.1. LCD1602介绍 •LCD1602&#xff08;Liquid Crystal Display&#xff09;液晶显示屏是一种字符型液晶显示模块&#xff0c;可以显示ASCII码的标准字符和其它的一些内置特殊字符&#xff0c;还可以有8个自定义字符 •显示容量&#xff1a;162个字符&#xff0c…

二、Maven安装

Maven安装 一、Centos7.9安装1.下载2.安装3.设置国内镜像4.设置maven安装路径 一、Centos7.9安装 1.下载 第一种&#xff1a;官网下载最新版本&#xff1a;http://maven.apache.org/download.cgi第二种&#xff1a;其他版本下载&#xff1a;https://archive.apache.org/dist/…

数据中心的新动脉:探索ADOP 800G MPO/MTP单模光纤跳线

在数据传输领域&#xff0c;随着技术的不断进步&#xff0c;对于更快、更高效的传输解决方案的需求日益增长。ADOP 800G MPO/MTP单模光纤跳线便是在这样的背景下应运而生的产品。本文将为您提供一个全面而详细的介绍&#xff0c;带您深入了解这款创新产品的特点、优势以及应用场…

国产主流数据库存储类型简析

国产数据库在技术架构上主要分为集中式、基于中间件分布式和原生分布式架构&#xff0c;衍生出集中式架构和分布式架构。那么在这些部署架构中&#xff0c;从数据分布的视角来看&#xff0c;在数据库中数据分布的形态是怎样的。本文将简要分析OceanBase、PolarDB、OpenGauss、G…

探索数据中台的力量:企业数据资产管理的未来_光点科技

随着数字化时代的到来&#xff0c;"数据中台"这一概念逐渐为人所知&#xff0c;并迅速成为推动企业数据驱动转型的重要基石。数据中台不仅是集数据接入、管理、分析于一身的综合平台&#xff0c;更是企业实现数据资源集中管理、分析决策和业务创新的核心支撑。 一、数…

【好书推荐6】《Excel函数与公式应用大全for Excel 365 Excel 2021》

【好书推荐6】《Excel函数与公式应用大全for Excel 365 & Excel 2021》 写在最前面《Excel函数与公式应用大全for Excel 365 & Excel 2021》关键点内容简介作者简介前言/序言目录 &#x1f308;你好呀&#xff01;我是 是Yu欸 &#x1f30c; 2024每日百字篆刻时光&…

预付费水表充值系统

预付费水表充值系统是一种为用户提供水费预付服务的系统&#xff0c;旨在方便用户预先充值水费&#xff0c;实现用水时按照预付金额自动扣费&#xff0c;从而实现水费的实时管理和节约用水。这种系统在社会生活中越来越受欢迎&#xff0c;下文将从核心功能、工作流程、优势特点…

SSRF题目进阶+SSRF正则绕过+进制绕过SSRF限制

[题目信息]&#xff1a; 题目名称题目难度SSRF题目进阶3 [题目考点]&#xff1a; SSRF绕过[Flag格式]: SangFor{h3bvbopR6L2EQOXv}[环境部署]&#xff1a; docker-compose.yml文件或者docker tar原始文件。 docker-compose up -d[题目writeup]&#xff1a; 1、实验主页 …

学习大数据的第一天

今天学习如何安装hapood安装 1.安装hapood安装 2.需要的资料 3.开始安装 1.创建目录 mkdir -p /export/server 2.进入目录下 cd /export/server/ 3.安装 安装需要的依赖 yum install gcc gcc-c make autoconf automake libtool curl lzo-devel zlib-devel openssl opens…

算法与数据结构要点速学——排序算法

排序算法 所有主要的编程语言都有一个内置的排序方法。假设并说排序成本为 O(n*log n)&#xff0c;通常是正确的&#xff0c;其中 n 是要排序的元素数。为了完整起见&#xff0c;这里有一个图表&#xff0c;列出了许多常见的排序算法及其完整性。编程语言实现的算法各不相同&a…

DBUtils工具类的使用

1、DBUtils是什么 为了更加简单地使用JDBC&#xff0c;Apache组织提供了一个DBUtils工具&#xff0c;它是操作数据库的一个组件&#xff0c;实现了对JDBC的简单封装&#xff0c;可以在不影响数据库访问性能的情况下简化JDBC的编码工作量。DBUtils工具要有2个作用。 写数据&am…