Learning C++ No.17【STL No.7】双端队列
引言:
北京时间:2023/3/17/7:18,刚刚快乐的早锻炼回来(不对 ,应该说回来有一会了),因为此时我已经吃完早饭,洗过澡了;现在回想起上学期,就算是第二天需要晨跑(6点起床),但我依然毫不畏惧,博客没写完,或者视屏没看完,我都会硬刚(有时到凌晨2-3点),但大部分时间都是硬刚都1点左右,然后写完博客(顺带会发个朋友圈,哈哈哈!(浏览量)),然后快乐的去睡觉, 然后就算6点起床,也不怎么当回事(但是这样是不好滴),长逗严重,所以当时我对这个早锻炼活动可以说是万般的厌恶(认为,这种活动结束后,大家不都是直接回到宿舍,然后继续睡吗?这不是影响我们的作息吗?有什么意义呢?),但是现在,我发现我的看法改变了(猜我现在在干嘛!),你答对了,我现在在码字,哈哈哈!所以现在的我,再也不熬夜了,每天准时在时间范围内睡觉,早起虽然依旧难受,但是,活动完之后,我发现,我多了一个早上的时间(7点-10点)进行码字,只是把写博客、看视屏的时间给调整了一下而已(主要不长逗),开心!虽然此时我觉得这个活动还是那么不聪明的样子,但是起码不厌恶了,所以我明白了一个道理(可能以前明白过),可能只是没有那么深,就是当我们无法改变环境的时候,我们只能去适应环境,通过体验,调整出一个更适合自己生存的环境,俗话说的好,识时务者为俊杰!所以适应环境真的非常重要(不要逆天改命哦!小伙伴们),OK!充分意识到引言写太长了,但是没关系,管他那么多呢?上天安排的最大嘛!(曾经有一份真诚的爱情放在我面前,我没有珍惜,等我失去的时候才后悔莫及,人世间最痛苦的事莫过于此。 如果上天能够给我一个再来一次的机会,我会对那个女孩子说三个字:我爱你!),哦!,不!,不!,不!紫霞!完蛋,电视看太多了,(也可能是星爷的电视太过于经典),导致一涉及就容易无法自拔,哈哈哈!OK!真的不能搞笑了,这篇博客的引言真的已经要不属于引言的范畴了(爱怎么写怎么写,略略略!),今天我们就继上篇博客中栈和队列的知识,来聊一聊栈和队列的经典适配器,双端队列(deque)和有关优先级队列(priority queue)的知识,当然重点还有一个叫仿函数的东西哦!(STL六大巨头之一,可不是泛泛之辈,尔等岂敢小瞧),目标明确,Here we go ,Here we go !(看到这句,我总是会联想到有些美剧中的经典枪战场景(不置可否!))

上篇博客我们学习了STL中的两个容器适配器,栈和队列,并且发现,栈和队列的学习成本不高(数据结构和string、vector、list),因为我们已经了解了很多有关知识,所以轻轻松松就把栈和队列的知识给搞定了,并且我们解决了两个经典的有关栈的题目,获取最小栈和栈的弹出,发现有的题目就是为栈这个数据结构设计的,使用栈来解决这种问题非常的合适,所以接下来,我们再来看两个有关栈的经典题目。
1.计算逆波兰表达式
题目:逆波兰表达式
例:我们平时算加减乘除时,把 1+2*3-4叫做是中缀表达式,但是中缀表达式并不可以通过栈的方式(入栈出栈)实现4则运算,所以需要把中缀表达式给转化成后缀表达式,例:1 2 3 * + 4 -(后缀表达式),这样就可以很好的利用栈的输入输出来实现四则远算了,但此时我们先不探讨如何将一个后缀表达式转换成中缀表达式,我们先探讨一下如何使用栈的方式来计算一个后缀表达式(逆波兰表达式)
解题思路:
- 遇到操作数就入栈 (
1 2 3 4 5) - 遇到操作符(
"+" "-" "*" "/")就取栈顶和栈顶后一个两个操作数进行运算,运算结果重新入栈 - 最后栈中的数据就是我们的结果(切记,此时是使用后缀表达式(逆波兰表达式)进行的运算),不存在运算符优先级问题
注意:加和乘由于运算关系不改变,所以没事,但是减和除会出问题,所以在出数据的时候,要把栈顶数据放到运算符的右边,栈顶后一个数据放到左边
代码实现如下:
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<stack>
using namespace std;class Solution
{
public:int evalRPN(vector<string>& tokens){stack<int> st;for (auto& str : tokens){if (str == "+" || str == "-" || str == "*" || str == "/"){int right = st.top();st.pop();int left = st.top();st.pop();switch (str[0])/{case'+':st.push(left + right);break;case'-':st.push(left - right);break;case'*':st.push(left * right); break;case'/':st.push(left / right); break;}}else{st.push(stoi(str));}}return st.top();}
};
代码细节注意点:
- 因为题目给给我们的是一个后缀表达式(以字符串的形式),并且因为switch case语句中的case只能针对于整形数据(char属于整形一簇(强转)),所以我们在遍历字符串中的字符数据时,此时就不可以直接用switch接收,所以只能使用if语句进行判断
- 并且由于减和除运算符的特性,所以要区别左操作数和有操作数
- 使用stoi函数(string类),可以直接将一个字符类型的数据转换为整形数据
- 不要把case语句中的break给漏掉了(case和break是需要匹配使用的,作用就是类似于else if语句)
2.中缀表达式转后缀表达式题目
搞定了上述的这个题目,此时我们就来看一下这个题目的前提,就是如何把一个中缀表达式转换成后缀表达式,当然前提还是在使用栈,通过栈来判断各个运算符的优先级,进而构成后缀表达式
例:将中缀1 + 2 * 3 - 4转换成后缀,1 2 3 * + 4 -
原理:
a. 如果是操作数,就直接存储到字符串数组中(string)
b. 如果是操作符(入栈)但是要分成两种情况:
- 栈为空(入栈)
- 栈不为空,(重点),让这个操作符去和栈顶的操作符进行比较,如果这个操作符的优先级高,就将该操作符入栈(不进行运算的原因是,防止还有优先级更高的操作符),但是如果这个操作符的优先级低或者相等,那么此时就要把栈顶的操作符出栈,然后进行运算(原因:该操作符优先级比上一个栈顶中的操作符高,比下一个操作符的优先级也高)那么此时它就是三个操作符中优先级最高的,那么此时出栈,对该运算符进行运算,并且此时栈顶的运算符得到运算之后,该优先级低的操作符需要和下一个操作符继续比较(递归实现),同理,高就入栈,低就出栈顶,然后继续递归比较
代码实现如下:
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<stack>
using namespace std;bool IsPra(char c)
{return (c == '(' || c == ')') ? true : false;
}
int GetPriority(char c)
{switch (c) {case '+':case '-':return 0; break;case '*':case '/':return 1; break;case '(':case ')':return -1; break;default:cout << "输入其他运算符\n" << endl;}
}
void CheckPriority(char c, stack<char>& operator_s, string& suffix)
{if (operator_s.empty()) {operator_s.push(c);return;}if (IsPra(c)) {if (c == '(') {operator_s.push(c);}else {// 弹出所有元素直到遇到左括号while (operator_s.top() != '('){suffix += operator_s.top();operator_s.pop();}// 遇到左括号,弹出且不加入后缀表达式operator_s.pop();}}else {// 如果不是括号,比较当前元素,与栈顶元素的优先级if (GetPriority(c) > GetPriority(operator_s.top())){operator_s.push(c);}else {suffix += operator_s.top();operator_s.pop();//递归方式循环比较check(c, operator_s, suffix);}}
}
string InfixToSuffix(string& infix)
{stack<char> operator_s; // 运算符栈string suffix; // 后缀表达式for (int i = 0; i < infix.size(); ++i) {if (infix[i] >= '0' && infix[i] <= '9'){suffix += infix[i];}else {CheckPriority(infix[i], operator_s, suffix);}}// 对中缀表达式的遍历结束,将栈中元素加入后缀表达式while (!operator_s.empty()) {suffix += operator_s.top();operator_s.pop();}return suffix;
}
如上代码,分为四个函数实现:
- 第一个函数
bool IsPra(char c),判断中缀表达式中是否有括号; - 第二个函数
int GetPriority(char c)固定运算符的优先级; - 第三个函数
void CheckPriority(char c, stack<char>& operator_s, string& suffix)对运算符的优先级进行判断(代码核心实现),也就是进行数据的比较; - 第四个函数
string InfixToSuffix(string& infix),合并操作数和操作符(栈中已经排好序了),此时就得到了我们想要的后缀表达式,可以直接通过栈,实现计算的后缀表达式。
并且注意:
此时解决括号问题的方法有非常的多,如上述代码使用的,将左括号和右括号的优先级都设置成最低,并且在入栈时,不需要进行比较(特殊处理),直接入栈,然后同理,将括号之间的操作符进行比较,最终当右括号(“)”)遇到左括号(“(”)时,将左括号弹出(删除栈顶元素),继续同理往下走;
但是这里也可以使用别的方法,例如:使用一个flag=0,如果遇到括号让flag=1,然后在flag=1的前提下,遇到的下一个运算符的优先级就是最高的(无论加减),等一系列的方法都可以,只要你控制得住;
所以我们的思路是:
()优先级最低
( 特殊处理(不比较直接入栈)
) 参与比较,直到遇到左括号,弹出左括号
总: 中缀转后缀利用栈的目的就是进行运算符的优先级的比较(遍历运算字符串,然后通过栈比较出运算符的优先级),然后通过入栈和出栈,进行操作数和操作符的运算,所以栈的用处在这些题目方面是非常的适用的,我们应该合理使用栈的特性(先进后出)。
浅谈双端队列(deque)
搞定了上述的题目,栈和队列的知识也就告一段落了, 并且在上篇博客中,我们说过栈和队列是使用一个叫deque的容器适配器实现的,所以此时我们就来看一看什么是deque;首先无论是在以前的迷宫题目,还是vector题目,我们都学习过类似二维数组的结构,或者说是vector<vector<int>>的结构,所以在此类型结构之下,我们来学学deque的结构 (list+vector) ,显然是与我刚刚所说的两个结构类似,如下图:

如上图,可以看出,我们是使用了一个指针数组来存放很多的小数组的地址,这样,我们就可以通过控制指针数组中指针指向的地址的空间来控制小数组中的数据了,并且发现,如果存储的数据量非常大,需要扩容,此时就不再需要像vector一样,开辟一个扩容之后的空间,然后把原来空间的数据拷贝,最后将原空间释放掉(目的:防止数据量太大太小);此时按照上图的结构,就可以直接对指针数组进行扩容,开辟更多的指针出来,消耗对于内置类型来说降低了整数倍,但是对于自定义类型来说就是降低了n倍(例:vector<vector<int>>类型的数据进行扩容)
所以此时我们就来谈谈上述结构的优点和缺点(deque),首先在学习其优缺点之前,我们先来复习一下vector和list的优缺点:
vector的缺点: 1.频繁扩容,消耗高 2. 头插头删效率很低
vector的优点: 1.使用下标支持随机访问 2.CPU的高速缓存效率高(涉及硬件知识)原因:地址连续
list的缺点:不支持随机访问
list的优点:任意位置插入删除效率高
复习了vector和list的优缺点之后,我们就来看一看它们的结合体deque的优缺点:
优点:如果使用vector和list实现了deque的话,此时deque的优点是:相比vector,扩容的代价低(只需要扩容指针就行,指针指向的数组固定);头插头删,尾插尾删效率都很高(删除头指针指向的数组中的第一个数据就行);并且也支持随机访问(例:每一个指针数组中的指针指向的是一个100个数据的vector数组,那么此时就以 /100为找行 %100位找列)此时就找到下标位置了(表示第几个指针数组中的指针,中的第几个下标)
缺点:不支持中间插入删除(如果支持中间插入删除缺点就是不支持随机访问) 并且如果要支持中间插入和删除的话,那么此时它的效率是比vector高,但是比list低;功能方面不够极致(没有vector的随机访问,也没有list的插入删除数据)的效率
所以,综上可以发现,deque的优缺点导致它不怎么适合直接用于存储数据,而是适合做一些容器适配器的适配器,例:栈和队列的适配器,这也就是为什么栈和队列的适配器是deque
deque的迭代器
搞定了上述的知识,我们来看一下,deque这个聚集了list和vector优点于一身的家伙,它的迭代器是怎样实现的吧!如下图:

从上图可以看出,deque的迭代器是比较复杂的,使用了4个原生指针,这里各个指针的作用,不多做讲解,感兴趣的同学,点击该链接deque迭代器指针的作用
此时了解了双端队列(deque)和deque的迭代器,下篇博客我们就学习自我实现好吧!
什么是优先级队列
搞定了双端队列的基本结构和优缺点,此时我们再来学习一下另一个队列,优先级队列,如下图:

从图中可以发现,优先级队列建的是一个大堆,所以如果我们想要建一个小堆的话,此时就可以引入一个叫仿函数的概念,也就是STL的六巨头之一,greater<int>,具体是什么东西,我们在之后的学习中在深入了解。
浅谈仿函数
template<class T>
struct Less//仿函数
{bool operator()(const T& x, const T& y){return x < y;}
};
template<class T>
struct greater//仿函数
{bool operator()(const T& x, const T& y){return x > y;}
};int main()
{Less<int> lessFunction;//此时就是让Less这个类的对象可以像是一个函数一样,具有很多的功能(本质上是,去调用了各种的运算符重载,或者是嵌套的函数)cout << lessFunction(1, 2) << endl;//此时这句代码的本质是去调用运算符重载,但是看起来像是去调用了一个函数return 0;
}
上述的代码就是仿函数greater和less的实现方式,本质上是通过我们的运算符重载实现具体的功能!这里不深入学习,了解即可

总结:STL中还有很多的东西需要我们学习,无论是迭代器还是仿函数,还是map、set容器,还是空间配置器(内存池),任务艰巨!
相关文章:
Learning C++ No.17【STL No.7】双端队列
引言: 北京时间:2023/3/17/7:18,刚刚快乐的早锻炼回来(不对 ,应该说回来有一会了),因为此时我已经吃完早饭,洗过澡了;现在回想起上学期,就算是第二天需要晨跑…...
Snackbar
1.简介 位于底部的提示View 支持侧滑消失 同一时间只有一个 不支持跨Activity展示 国内使用率很低 2.基础使用 2.1 基本展示 Snackbar.make(view, "Content", Snackbar.LENGTH_LONG).show()2.2 设置点击事件 注意不设置点击事件回调,点击按钮的文字不…...
HummerRisk 使用教程:主机检测
1. 概述 HummerRisk 是开源的云原生安全平台,以非侵入的方式解决云原生环境的安全和治理问题。核心能力包括混合云的安全治理和容器云安全检测。 本文将介绍HummerRisk中的主机检测部分功能,包括如何管理主机、管理凭证,以及使用主机检测规…...
【Arduino无线气象站项目】
【Arduino无线气象站项目】 1. 概述2. Arduino无线气象站电路图3. 定制设计电路板4. Arduino无线气象站代码5. 总结1. 概述 使用DHT22传感器测量室外温度和湿度,并使用NRF24L01收发器模块将这些数据无线发送到室内机。在室内机,还有另一个用于测量室内温度和湿度的DHT22传感…...
HTTP详解
一,什么是HTTPHTTP(全称为“超文本传输协议”),是一种应用非常广泛的应用层协议,之前在《初识网络原理》的博客(初识网络原理_徐憨憨!的博客-CSDN博客)中,有详细讲解过TCP/IP五层模型,其中应用层描述了数据…...
cpufreq--处理器功耗控制
cpu 功耗控制 参考框架: cpufreq 框架。 cpufreq 框架提供 cpu 功耗管理接口,以及功耗管理方案。 用户可以通过功耗管理接口(以文件形式提供)来选择管理方案,并设置相关参数。 管理方案的实现则由具体的驱动来完成。…...
做技术,最忌讳东张西望
又好长时间没更新,研二了,忙着做实验、写论文、发论文,再加上给我导做一些事情(都习惯了,以前很不爽的事情,现在居然能这么平静的说出来)。 但这不是我今天说的重点,而是另外一件事…...
Oracle 常见报错问题汇总
Oracle 常见报错问题汇总 报错:ORA-01017: invalid username/password; logon denied报错:ORA-01031: insufficient privileges报错:"ORA-01034: ORACLE not available" 和 "ORA-27101: shared memory realm does not exist"报错:“ORA-00119: invalid…...
单片机连接有人云上传数据
首先采用有人物联网的模块 ,连接有人云平台服务器 看云平台相关配置配置连接设备在线后 添加设备添加设备完成后 添加变量模板 变量模板的添加方式如下 :本次采用的是标准的MODbus 协议添加一个温度变量温度变量如下显示云平台 下发数据 采集01 03 00 00…...
系统集成项目管理工程师:第18章项目风险管理学习笔记
第18章项目风险管理 一、目录 18.1 风险概述 18.1.1 风险的定义 18.1.2 风险的分类 18.1.3 风险的性质 18.2 项目风险管理 18.3 规划风险管理 18.3.1 规划风险管理的输入 18.3.2 规划风险管理的工具与技术 18.3.3 规划风险管理的输出 18.4 识别风险...
【笔试强训选择题】Day3.习题(错题)解析
文章目录 前言一、Day3习题(错题)解析二、Day3习题(原题)练习总结前言 今天我们将进入到第三天的练习,希望能一直坚持下去,不断反思总结错误,得到进步; 一、Day3习题(错…...
基于GPT-4的免费代码生成工具
大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…...
Android开发的这一年里,Jetpack的Room源码是怎么狠狠奖励我的?
简述 Android Jetpack的出现统一了Android开发生态,各种三方库逐渐被官方组件所取代。Room也同样如此,逐渐取代竞品成为最主流的数据库ORM框架。这当然不仅仅因为其官方身份,更是因为其良好的开发体验,大大降低了SQLite的使用门槛…...
推荐一款卸载软件的小工具-《UninstallToo》
目录 UninstallToo介绍 UninstallToo下载 UninstallToo使用 总结 UninstallToo介绍 Uninstall Tool 是一款可以用来替代“添加/删除程序”的工具。它允许您显示隐藏的安装程序,按名称过滤已安装程序的列表,强行写在程序,浏览注册表项目&a…...
线程池——JUC随记8
线程池使用方式 1、一池N线程(Executors.newFixedThreadPool(n)) import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class ExecutorDemo {public static void main(String[] args) {ExecutorService execu…...
SpringCloudAlibaba微服务调用组件-Feign
SpringCloudAlibaba微服务调用组件-Feign 本项目代码与笔记已存放在Gitee仓库 地址: 代码,笔记 文章目录SpringCloudAlibaba微服务调用组件-Feign1. 什么是Feign1.1 优势2. Spring Cloud Alibaba快速整合OpenFeign1)引入依赖2)编写…...
高效学习方法论
2023.03.17 《程序员的三门课:技术精进、架构修炼、管理探秘 / 于君泽等著》学习笔记 学会学习一、高效学习的方法1、管理好自己的目标1)评估能力2)制定目标3)评估目标2、利用好碎片时间3、在同一时间只做一件事二、高效学习的途径…...
C语言结构体(一篇学会)
C语言结构体 在C语言中,结构体是一种自定义的数据类型,它允许用户将不同类型的数据组合在一起。结构体由多个变量组成,这些变量称为结构体的成员。结构体成员可以是不同的数据类型,如整数、浮点数、字符或其他结构体等。 结构体…...
嵌入式软件开发之Linux下C编程
目录 前沿 Hello World! 编写代码 编译代码 GCC编译器 gcc 命令 编译错误警告 编译流程 Makefile 基础 何为 Makefile Makefile 的引入 前沿 在 Windows 下我们可以使用各种各样的 IDE 进行编程,比如强大的 Visual Studio。但是在Ubuntu 下如何进…...
普通Java工程师 VS 优秀架构师
1 核心能力 1.1 要成为一名优秀的Java架构师 只懂技术还远远不够,懂技术/懂业务/懂管理的综合型人才,才是技术团队中的绝对核心。 不仅仅是架构师,所有的技术高端岗位,对人才的综合能力都有较高的标准。 架构路线的总设计师 规…...
7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
