C++的 stack和queue 的应用和实现【双端队列的理解和应用】

文章目录

  • stack的理解和应用
    • 栈的理解
    • 栈的模拟实现
      • string实现stack
      • vector实现stack
  • queue的理解和应用
    • 队列的理解
      • 队列的模拟实现
  • 双端队列
    • 原理的简单理解
    • deque的缺陷
    • 为什么选择deque作为stack和queue的底层默认容器
    • STL标准库中对于stack和queue的模拟实现
      • stack的模拟实现
      • queue的模拟实现

stack的理解和应用

栈是一种后进先出(Last In, First Out, LIFO)的数据结构,类似于向弹夹里面装子弹和卸子弹。在C++中,栈通常用于实现递归算法、表达式求值、括号匹配等场景。

栈的理解

在C++中,std::stack 是一个容器适配器,它提供了栈的功能。它基于其他容器(如 std::vectorstd::deque 等)实现,但只暴露了栈的接口。栈的基本操作包括:

  • push():向栈顶添加一个元素。
  • pop():移除栈顶的元素。
  • top():访问栈顶元素,但不移除它。
  • empty():检查栈是否为空。
  • size():返回栈中元素的数量。

下面通过一段代码来进行理解

void usestack()
{stack<int> st;//栈的创建st.push(1);//入栈st.push(2);st.push(3);st.push(4);st.push(5);cout << st.size() << endl;//返回栈中的元素的数量while (!st.empty())//判断栈是否为空{cout << st.top() << " ";//输出栈顶元素st.pop();//出栈}cout << endl;
}

栈的模拟实现

栈的实现通常使用一个动态数组或者链表来存储数据,但栈的接口只允许从栈顶进行操作。
所以通常我们进行实现stack直接用vector或者string来实现就非常的方便

string实现stack

#include <iostream>
#include <string>
#include <stdexcept>class StringStack {
private:std::string data;public:void push(const std::string& item) {data += item + " ";}void pop() {if (empty()) {throw std::out_of_range("Stack<>::pop(): empty stack");}size_t lastSpace = data.rfind(' ');if (lastSpace != std::string::npos) {data.erase(lastSpace);} else {data.clear();}}std::string top() const {if (empty()) {throw std::out_of_range("Stack<>::top(): empty stack");}size_t lastSpace = data.rfind(' ');return data.substr(lastSpace + 1);}bool empty() const {return data.empty();}size_t size() const {return data.size();}
};

vector实现stack

#include <iostream>
#include <vector>
#include <stdexcept>template <typename T>
class VectorStack {
private:std::vector<T> data;public:void push(const T& item) {data.push_back(item);}void pop() {if (empty()) {throw std::out_of_range("Stack<>::pop(): empty stack");}data.pop_back();}T top() const {if (empty()) {throw std::out_of_range("Stack<>::top(): empty stack");}return data.back();}bool empty() const {return data.empty();}size_t size() const {return data.size();}
};

这样就可以很好的模拟实现stack

queue的理解和应用

队列是一种先进先出(First In, First Out, FIFO)的数据结构,它类似于排队等候服务的队伍:新来的人排在队伍的末尾,而服务总是从队伍的最前面开始。在C++中,队列通常用于任务调度、消息队列、打印任务管理等场景。

队列的理解

在C++中,std::queue 是一个容器适配器,它提供了队列的功能。它基于其他容器(如 std::dequestd::list 等)实现,但只暴露了队列的接口。队列的基本操作包括:

  • enqueue():向队列尾部添加一个元素。
  • dequeue():移除队列头部的元素。
  • front():访问队列头部元素,但不移除它。
  • empty():检查队列是否为空。
  • size():返回队列中元素的数量。

下面通过一段代码来进行理解

void usequeue()
{queue<int> qu;qu.push(1);qu.push(2);qu.push(3);qu.push(4);qu.push(5);cout << qu.size() << endl;cout << qu.back() << endl;//输出队列最后一个元素while (!qu.empty())//判断队列是否为空{cout << qu.front() << " ";//输出队列头元素qu.pop();//出队列}cout << endl;
}

队列的模拟实现

队列的实现通常使用一个动态数组或者链表来存储数据,但队列的接口只允许从队列尾部进行添加操作,从队列头部进行移除操作。
因为string在头插和头删上实际拿上大大的增加,所以一般实现queue就用strack进行

#include <iostream>
#include <list>
#include <stdexcept>template <typename T>
class ListQueue {
private:std::list<T> data;public:void enqueue(const T& item) {data.push_back(item);}void dequeue() {if (data.empty()) {throw std::out_of_range("Queue<>::dequeue(): empty queue");}data.pop_front();}T front() const {if (data.empty()) {throw std::out_of_range("Queue<>::front(): empty queue");}return data.front();}bool empty() const {return data.empty();}size_t size() const {return data.size();}
};

双端队列

在实现栈和队列的时候liststing或多或少都会有一些问题或者小毛病那有没有一个老大哥呢?这时候就引出了一个新的容器适配器
deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高。
在这里插入图片描述

原理的简单理解

那么这个老大哥是怎么进行实现的呢?

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

双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂,如下图所示:
在这里插入图片描述
那deque是如何借助其迭代器维护其假想连续的结构呢?
通过一个中控器(图中map)来进行多个buffer的连接进而实现”连续空间“然后通过两个迭代器startfinish进行查找和遍历每个迭代器都有四个节点curfirst,last分别是在buffer指向任意位置,头尾的。而node是指向中控器进而连接下一个bufferfinsh指向随后一个buffer的最后一个元素二点下一个位置进行结尾的定位
在这里插入图片描述

deque的缺陷

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

所以这么看这个老大哥也不是万能的

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

stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()pop_back()操作的线性结构,都可以作为stack的底层容器,比如vectorlist都可以;queue是先进先出的特殊线性数据结构,只要具有push_backpop_front操作的线性结构,都可以作为queue的底层容器,比如list。但是STL中对stackqueue默认选择deque作为其底层容器,主要是因为:

  1. stackqueue不需要遍历(因此stackqueue没有迭代器),只需要在固定的一端或者两端进行操作。
  2. 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,deque不仅效率高,而且内存使用率高。
    结合了deque的优点,而完美的避开了其缺陷。

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

stack的模拟实现

#include<deque>
namespace bite
{template<class T, class Con = deque<T>>//template<class T, class Con = vector<T>>//template<class T, class Con = list<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:Con _c;};
}

queue的模拟实现

#include<deque>
#include <list>
namespace bite
{template<class T, class Con = deque<T>>//template<class T, class Con = list<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:Con _c;};
}

感觉好的话点个赞
请添加图片描述

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

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

相关文章

CSRF介绍及Python实现

CSRF 文章目录 CSRF1. CSRF是什么&#xff1f;2. CSRF可以做什么&#xff1f;3. CSRF漏洞现状4. CSRF的原理5. 举例说明6. CSRF的防御Python示例 1. CSRF是什么&#xff1f; CSRF&#xff08;Cross-Site Request Forgery&#xff09;&#xff0c;中文名称&#xff1a;跨站请求…

来get属于你的达坦科技令人心动的offer吧!

我们是谁 达坦科技始终致力于打造高性能Al Cloud 基础设施平台DatenLord&#xff0c;积极推动AI应用的落地。DatenLord通过软硬件深度融合的方式&#xff0c;提供高性能存储和高性能网络。为AI 应用提供弹性、便利、经济的基础设施服务&#xff0c;以此满足不同行业客户对AICl…

网络规划(homework 静态路由 and Rip路由表更新)

1、写出下图路由器1和路由器3中的路由表&#xff08;按直接交付、特定主机交付、特定网络交付、 默认交付的顺序放置路由项&#xff09; 2、写出Ri更新后的路由表&#xff08;rip路由协议&#xff09; 1、将Rj广播的路由消息全部1 2、直接对照着更新Ri中的路由表

SQLite字节码引擎(十二)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLite的架构&#xff08;十一&#xff09;&#xff08;&#xff09; 下一篇&#xff1a;SQLite—系列文章目录 1、 摘要 SQLite 的工作原理是将 SQL 语句转换为字节码和 然后在虚拟机中运行该字节码。本文档 …

车载电子电器架构 —— 工程EOL诊断

车载电子电器架构 —— 工程EOL诊断 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己…

Navicat工具使用

Navicat的本质&#xff1a; 在创立连接时提前拥有了数据库用户名和密码 双击数据库时&#xff0c;相当于建立了一个链接关系 点击运行时&#xff0c;远程执行命令&#xff0c;就像在xshell上操作Linux服务器一样&#xff0c;将图像化操作转换成SQL语句去后台执行 一、打开Navi…

CCF-CSP19<2020-06>-第1/2题

本次难度&#xff1a; 202006-1 线性分类器 题目&#xff1a;202006-1 题目分析&#xff1a; 给定n个点&#xff0c;并标记为AB两类&#xff0c;问给定直线是否能将其分为两个点集。 简单数学知识&#xff0c;点在直线上满足axbyc0&#xff0c;点在直线割平面所得的上下其…

云备份day02

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;C云备份项目 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 主要内容介绍了第三方库jsoncpp和bundle库的使用 文章目录 云备…

android 使用ollvm混淆so

使用到的工具 ndk 21.4.7075529&#xff08;android studio上下载的&#xff09;cmake 3.10.2.4988404&#xff08;android studio上下载的&#xff09;llvm-9.0.1llvm-mingw-20230130-msvcrt-x86_64.zipPython 3.11.5 环境配置 添加cmake mingw环境变量如下图: 编译 下载…

Codeforces Round 836 (Div. 2) D. Range = √Sum

题目 思路&#xff1a; #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e6 5, inf 1e18, maxm 4e4 5; c…

PyQt qrc2py 使用PowerShell将qrc文件转为py文件并且将导入模块PyQt或PySide转换为qtpy模块开箱即用

前言 由于需要使用不同的qt环境&#xff08;PySide&#xff0c;PyQt&#xff09;所以写了这个脚本&#xff0c;使用找到的随便一个rcc命令去转换qrc文件&#xff0c;然后将导入模块换成qtpy这个通用库(支持pyside2-6&#xff0c;pyqt5-6)&#xff0c;老版本的是Qt.py(支持pysi…

【力扣每日一题】1026. 节点与其祖先之间的最大差值

LC 1026. 节点与其祖先之间的最大差值 题目描述 给定二叉树的根节点 root&#xff0c;找出存在于 不同 节点 A 和 B 之间的最大值 V&#xff0c;其中 V |A.val - B.val|&#xff0c;且 A 是 B 的祖先。 &#xff08;如果 A 的任何子节点之一为 B&#xff0c;或者 A 的任何子…

算法刷题Day23 | 回溯算法基础理论、 77. 组合

目录 0 引言1 回溯算法基础理论1.1 回溯算法模板1.2 2 组合2.1 我的解题2.2 剪枝操作 &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;算法专栏&#x1f4a5; 标题&#xff1a;算法刷题Day23 | 回溯算法基础理论、 77. 组合❣️ 寄语&#xff1a;书…

贪心算法|455.分发饼干

力扣题目链接 class Solution { public:int findContentChildren(vector<int>& g, vector<int>& s) {sort(g.begin(), g.end());sort(s.begin(), s.end());int index s.size() - 1; // 饼干数组的下标int result 0;for (int i g.size() - 1; i > 0;…

简约好用的TCPUDP小工具

csdn下载地址&#xff1a; https://download.csdn.net/download/a876106354/89077745

木棍【dfs搜索优化】

木棒 题目描述 输入样例&#xff1a; 9 5 2 1 5 2 1 5 2 1 4 1 2 3 4 0输出样例&#xff1a; 6 5【思路】 优化 【AC代码】 #include <iostream> #include <algorithm> #include <cstring>using namespace std;const int N 70;int w[N], sum, length,…

阿里云邮件服务器多少钱?邮件服务器租用费用

阿里云邮件服务器租用费用&#xff0c;2核2G3M带宽99元一年、2核4G4M服务器199元一年&#xff0c;不只是云服务器ECS&#xff0c;还可以选择轻量应用服务器。 0、在阿里云CLUB中心领取 aliyun.club 当前最新的优惠券和服务器报价单 1、阿里云服务器ECS经济型e实例&#xff0c;2…

专题三——二分算法

目录 原理 模板 朴素二分算法 非朴素二分算法 一二分查找 二在排序数组中查找元素的第一个和最后一个位置 三点名 四x的平方根 五搜索插入位置 六山脉数组的峰顶索引 七寻找峰值 八寻找旋转排序数组中的最小值 原理 定义两个指针&#xff1a;left指向数组第一个元…

Redis -- 缓存雪崩问题

缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机&#xff0c;导致大量请求到达数据库&#xff0c;带来巨大压力。 可能原因 : 同一时间大量的key到期 ; 解决方案&#xff1a; 给不同的Key的TTL添加随机值 利用Redis集群提高服务的可用性 给缓存业务添加降…

渗透测试靶机----Raven-1

渗透测试靶机----Raven-1 开启靶机&#xff0c;登录窗&#xff0c;平平无奇 开扫&#xff1a; 先看看ip 这里发现192.168.217.166 发现相关服务端口&#xff0c;这里看到80&#xff0c;还是老样子&#xff0c;先80入手打开发现一个熟悉的站点&#xff1a; 这里可以使用漏扫工具…