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。5.下图所示是官方库中链表list、栈stack、队列queue的声明和栈stack、队列queue的介绍。严格地说,list是一种容器,而stack和queue是一种容器适配器,从下图可以看出,链表list声明的第二个参数是一个空间配置器,栈stack和队列queue的第二个参数是一个容器
栈stack、队列queue(包括后面的priority_queue)是容器适配器,也就是说他们不是自己原生实现的,他们是依靠包装容器适配出来的
1.2.stack类的常用接口说明
使用stack前的说明:
1.使用stack类需要包含stack的头文件,代码为:#include<stack>
注:这里不是包含stack.h是因为会和c语言的库函数冲突,不能直接包含stack.cpp是因为如果项目里面同时两个人都包含stack.cpp,那么就会造成重定义
2.stack类是在std里面的,因此使用stack类需要using namespace std将std展开(如果不展开每次使用stack类需要std::stack)
注:std和iostream是无关的,只不过帮助进行输入输出的cout、cin、enl等是在std里面定义的。如果不输入输出,不包含iostream也可以使用stack类,但是需要展开std
stack的接口:
函数名称
功能说明
stack() 构造空的栈 empty() 检测stack是否为空 size() 返回stack中元素的个数 top() 返回栈顶元素的引用 push() 将元素val压入stack中 pop() 将stack中尾部的元素弹出注:
1.stack里面没有迭代器,因为栈这种数据结构让你随便去遍历反而是不好的,如果随便遍历就保证不了后进先出的功能了,栈里面数据的遍历代码如下图所示
1.3.stack类练习题
练习题一:
题目描述:
题目链接:155. 最小栈 - 力扣(LeetCode)
思路:
思路1:建立两个栈st和minst,st是正常的栈存储数据,minst存储最小值数据。插入时插入到st栈中,插入的值如果大于minst栈top的值,则minst栈再插入top的值,插入的值如果小于minst栈top的值,则minst栈插入st栈插入的值。st栈pop删除数据的时候,minst也跟着删除数据。
例如:st插入5,目前最小值是5,则minst插入5;st插入8,目前最小值是5,则minst再插入5;st插入7,目前最小值是5,则minst再插入5;st插入2,目前最小值是2,则minst插入2,如下图一所示,st进行删除,那么minst也要进行删除,如下图二所示。像前面的操作如果想要得到栈的最小值,直接取minst的的top即可。
上面的思路可以优化一下,见下面思路2。
思路2:建立两个栈st和minst,st是正常的栈存储数据,minst存储最小值数据。插入时插入到st栈中,插入的值如果大于minst栈top的值,不做处理,插入的值如果小于minst栈top的值,则minst栈插入st栈插入的值。st栈pop删除数据的时候,如果st删除的数据大于minst栈top的值,不做处理,如果st删除的数据等于minst栈top的值(不存在st删除的数据大于minst栈top的值的情况),minst也跟着删除数据,如下图所示。
代码:
练习题二:
题目描述:
题目链接:栈的压入、弹出序列_牛客题霸_牛客网 (nowcoder.com)
思路:
入栈序列先入栈,每次入栈以后,栈顶跟出栈序列比较,有两种情况:
情况一:能匹配:出栈序列往后,出战。
情况二:不能匹配:有两种情况:(1)如果入栈序列没有走到尾,说明这个数据可能还没入栈,继续入栈(2)如果入栈序列已经走到尾,说明数据一定在栈中,只是顺序不对,出栈序列非法
例1:输入[1 2 3 4 5]和[4 5 3 2 1]序列
出栈序列指向4,入栈序列入栈1,和出栈序列4不匹配,入栈序列入栈2,和出栈序列4不匹配,入栈序列入栈3,和出栈序列4不匹配,入栈序列入栈4,和出栈序列4匹配,出栈序列指向5,入栈序列3,和出栈序列5不匹配,入栈序列入栈5,和出栈序列5匹配,出栈序列指向3,入栈序列3,和出栈序列3匹配,出栈序列指向2,入栈序列2,和出栈序列2匹配,出栈序列指向1,入栈序列1,和出栈序列1匹配,出栈序列没问题
例2:输入[1 2 3 4 5]和[4 3 5 1 2]序列
出栈序列指向4,入栈序列入栈1,和出栈序列4不匹配,入栈序列入栈2,和出栈序列4不匹配,入栈序列入栈3,和出栈序列4不匹配,入栈序列入栈4,和出栈序列4匹配,出栈序列指向3,入栈序列3,和出栈序列3匹配,出栈序列指向5,入栈序列2,和出栈序列5不匹配,入栈序列入栈5,和出栈序列5匹配,出栈序列指向1,入栈序列2,和出栈序列1不匹配,入栈序列已经走到尾,出栈序列非法
代码:
注:上面 return popi==popV.size 和 return st.empty() 两种结束条件都是可以的
练习题三:
题目描述:
题目链接:150. 逆波兰表达式求值 - 力扣(LeetCode)
思路:(后缀表达式计算的思路)
从前往后依次取数据:
(1)如果是操作数:将操作数入栈
(2)如果是运算符:取连续两个栈顶数据进行运算,运算结果继续入栈
数据走完将栈里的操作数出栈,出栈的值就是结果值
例:计算后缀表达式123*+的结果
首先遇到数据1,1是操作数将1入栈,此时栈里的数据为1,走到数据2,2是操作数将2入栈,此时栈里的数据为12,走到数据3,3是操作数将3入栈,此时栈里的数据为123,走到数据*,*是运算符,取连续两个栈顶数据3和2进行运算,结果为2*3=6(连续出两个先出来的是运算符的右操作数,后出来的是运算符的左操作数),将结果6入栈,此时栈里的数据为16,走到数据+,+是运算符,取连续两个栈顶数据6和1进行运算,结果为6+1=7,将7入栈,数据取完将栈里面操作数出栈就是结果,结果为7
代码:
注:
1.中缀表达式就是操作符在中间,后缀表达式就是操作符在后面(我们平时写的都是中缀表达式)
中缀表达式1+2*3转化为后缀表达式就是123*+,后缀表达式123*+的意思就是2和3相乘,然后1加上2和3相乘的结果。本题是给你后缀表达式,让你计算出结果。
2.本题是后缀表达式计算的问题。还有道题是中缀表达式计算的问题,中缀表达式计算问题的解法是先将中缀表达式转成后缀表达式,将计算后缀表达式的值即可,后缀表达式的计算问题我们已经会了,将中缀表达式转成后缀表达式的思路如下所示
思路:(中缀表达式转成后缀表达式的思路)
从前往后依次取数据:
(1)遇到操作数:将操作数输出或者存储(输出还是存 储是看转成后的表达式输出还是存储)
(2)遇到运算符:跟栈顶运算符进行比较,如果栈为空或者比栈顶运算符优先级高就将其入栈,然后再往下取数据;如果比栈顶运算符优先级低或者优先级一样就出栈顶的操作符,然后继续和此时栈顶运算符比较......
数据走完将栈里的运算符依次出栈
例:将中缀表达式1+2*3/2-5转成后缀表达式
首先遇到数据1,1是操作数进行存储,存储空间此时为1,走到数据+,+是运算符,此时栈为空运算符+入栈,走到数据2,2是操作数进行存储,存储空间此时为12,走到数据*,*是运算符,*运算符比栈顶+运算符优先级高,运算符*入栈,走到数据3,3是操作数进行存储,存储空间此时为123,走到数据/,/是运算符,/运算符和栈顶*运算符优先级相同,此时栈顶运算符*出栈,存储空间此时为123*,/运算符继续和此时栈顶运算符+比较,/运算符优先级高,运算符/入栈,走到数据2,2是操作数进行存储,存储空间此时为123*2,走到数据-,-是运算符,-运算符比栈顶/运算符优先级低,此时栈顶运算符/出栈,存储空间此时为123*2/,-运算符继续和此时栈顶运算符+比较,-运算符和+运算符优先级相同,此时栈顶运算符+出栈,存储空间此时为123*2/+,走到数据5,5是操作数进行存储,存储空间此时为123*2/+5,数据取完将栈里面运算符出栈,存储空间此时为123*2/+5-
2.标准库中的queue类
2.1.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类的常用接口说明
使用queue前的说明:
1.使用queue类需要包含queue的头文件,代码为:#include<queue>
注:这里不是包含queue.h是因为会和c语言的库函数冲突,不能直接包含queue.cpp是因为如果项目里面同时两个人都包含queue.cpp,那么就会造成重定义
2.queue类是在std里面的,因此使用queue类需要using namespace std将std展开(如果不展开每次使用queue类需要std::queue)
注:std和iostream是无关的,只不过帮助进行输入输出的cout、cin、enl等是在std里面定义的。如果不输入输出,不包含iostream也可以使用queue类,但是需要展开std
queue的接口:
函数名称
功能说明
queue() 构造空的队列 empty() 检测队列是否为空,是返回true,否则返回false size() 返回队列中有效元素的个数 front() 返回队头元素的引用 back() 返回队尾元素的引用 push() 在队尾将元素val入队列 pop() 将队头元素出队列注:
1.queue里面没有迭代器,因为队列这种数据结构让你随便去遍历反而是不好的,如果随便遍历就保证不了先进先出的功能了,队列里面数据的遍历代码如下图所示
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类的常用接口说明
使用priority_queue前的说明:
1.使用priority_queue类需要包含queue的头文件,代码为:#include<queue>,
注:priority_queue类的定义是在queue头文件里的
2.priority_queue类是在std里面的,因此使用priority_queue类需要using namespace std将std展开(如果不展开每次使用priority_queue类需要std::priority_queue)
注:std和iostream是无关的,只不过帮助进行输入输出的cout、cin、enl等是在std里面定义的。如果不输入输出,不包含iostream也可以使用priority_queue类,但是需要展开std
3.优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。注意:默认情况下priority_queue是大堆。
priority_queue的接口:
函数名称
功能说明
priority_queue() priority_queue(first, last) 构造一个空的优先级队列 empty( ) 检测优先级队列是否为空,是返回true,否则返回false top( ) 返回优先级队列中最大(最小元素),即堆顶元素 push(x) 在优先级队列中插入元素x pop() 删除优先级队列中最大(最小)元素,即堆顶元素注:
1.priority_queue优先级队列官方库里的声明和介绍如下图所示,priority_queue模板第二个参数的默认值vector,也就是说默认使用vector作为其底层存储数据的容器
priority_queue优先级队列第三个参数默认传的是仿函数less,传less其实是一个大堆(正常传greater才应该是大堆,这里是c++的一个失误),也就是说priority_queue优先级队列默认取的是大堆。如果想控制成小堆,优先级队列模板中第三个参数传greater仿函数即可。
2.priority_queue里面没有迭代器,因为堆这种数据结构让你随便去遍历反而是不好的,如果随便遍历就保证不了堆的功能了,优先级队列(堆)里面数据的遍历代码如下图所示
优先级队列默认情况下是一个大堆,如上图所示,如果要使用小堆,应该手动传priority_queue优先级队列模板的第二第三个参数,如下图所示