数据结构个人总结系列--栈与队列

目录

前言

模拟系列

LeetCode232题:用栈实现队列

LeetCode225题:用队列实现栈

匹配系列

1036: 括号匹配PIPIOJ

1229: 括号匹配PIPIOJ

1260: 相邻相同字母删除PIPIOJ

1360: 删除最外层括号PIPIOJ

1311: 删除最少无效括号PIPIOJ

1332: 反转括号之间的子串PIPIOJ

法一:括号匹配+反转

法二:括号预处理

栈在表达式中的运用

1362: 计算表达式PIPIOJ

1291: 中缀表达式转后缀表达式I(不带括号)PIPIOJ

1292: 中缀表达式转后缀表达式II(带括号)PIPIOJ

总结


前言

这一章的算法题主要还是运用栈的,队列的只有1个(模拟栈),因为这两个东西的运用主要渗透在了树和图的算法题里面,唯一比较需要下点功夫的就是单调栈了,但这跟我考研算法的考察没有什么关联,等上岸后,我会再回来补到这里的。同样由于编者水平有限,在讲解过程中难免会存在错误和不清晰的地方,以及一些没有考虑到的特殊情况,望读者指正!

模拟系列

LeetCode232题:用栈实现队列

用栈实现队列
 

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):

实现 以下几个函数:

void push(int x) 将元素 x 推到队列的末尾
int pop() 从队列的开头移除并返回元素
int peek() 返回队列开头的元素
boo empty() 如果队列为空,返回 true ;否则,返回 false

这道题相信大家应该都不陌生了,一道经典题型,不过我们也不能眼高手低,因为这是一道非常有助于我们理解栈功能的题目,深刻的体现了栈“后进先出”的特点。

思路:用栈模拟队列,首先用一个栈肯定是无法实现的,但是用一个队列实现栈这是可以做到的,神奇吧,下一道题就是了

回到本题,我们需要借助两个栈,第一个栈用来模拟队列的入口,简称“入口栈”。第二个栈用来模拟队列的出口,简称“出口栈”。为什么要这么做呢,由于队列的特点是先进先出,相当于“顺序”,而栈的特点就是“逆序”,那么“负负得正”,我们入队时将元素压进入口栈,在要执行出队操作时,再将元素全部压入到出口栈,这样出口1栈的元素出栈顺序就是顺序的了,如下图所示

 总体思路就出来了,但是细节方面我们还需再打磨一下,考虑特殊情况

承接上图,我们如果只是单纯先出队1,2,留下了3在里面,然后再入队4,5,那么我们再想出队4该怎么办呢?这时候就得先将3出队,再将入口栈元素全部压入出口栈,也就是说,在出口栈不空的情况下,只要我们遇到了需要出队“入口栈”里的元素指令,就需要先将出口栈里的元素全部出栈

 

 由于用栈实现队列的代码实现不好考虑样例,所以我在这里仅给出部分代码,大家可以自行通过我上面的LeetCode对应题目的链接敲一下

stack<int>stIn; //入口栈
stack<int>stOut;//出口栈
void push(int x) {  //入队操作
    stIn.push(x);
}

int pop() { //出队操作
    if(stOut.empty()){  //出口栈为空时,才能将入口栈的元素压进出口栈
        while(!stIn.empty()){   //必须全部压入
            int x=stIn.top();
            stOut.push(x);
            stIn.pop();
        }
    }
    int result=stOut.top(); //取栈顶元素出队
    stOut.pop();
    return result;
}

int peek() {    //返回队列开头元素
    int result=pop();   //直接使用上述已定义的pop函数最方便
    stOut.push(result); //同时我们也要将它压回出口栈
    return result;
}

bool empty() {  //判断队列是否为空
    return stIn.empty()&&stOut.empty(); //两栈均空,队列才为空
}

LeetCode225题:用队列实现栈

225. 用队列实现栈

请你使用队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。

void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

这道题就很有意思了,按照上题的经验来说,我们同样需要用两个队列实现栈,但是由于队列的“双开口”特性,这道题居然可以只用一个队列来实现栈(两个队列也可以),看到这里可以先仔细想想该怎么做,再看看我的思路,给大家一个分割点。

 

思路:如下图为例,我们现在入栈了1,2,3,那么要出栈的话,只能是3,2,1的顺序出栈,但是队列的顺序是1,2,3,该怎么做呢?不要忘了,我们可是学过循环队列的男人!!!是不是一下子就思路通畅了,我们只需要知道队列的长度,那么我们先一直出队,再入队,直到队尾的时候停止,那么此时的队列顺序是不是就变成了3,2,1,于是这道题就被我们完美A掉啦

有一点需要注意下,有的同学可能会直接取队尾元素,然后循环直到遇到队尾元素停止,看似好像没什么问题,但实际上队列内的元素是有可能一样的,比如2222222,全是2,那循环根本不会开始

 同样的,由于模拟的过程不方便自定数据和操作,在这里我也只给出算法代码,大家可以到上面LeetCode题目对应链接去敲敲试试。

    queue<int>Que;
    MyStack() {

    }
    
    void push(int x) {  //入栈操作
        Que.push(x);
    }
    
    int pop() { //出栈操作
        int length=Que.size();
        length--;   
        while(length--){    //只需要循环length-1次
            int x=Que.front();  //取队头
            Que.pop();
            Que.push(x); //压回队尾
        }
        int result=Que.front(); //此时的队头就是栈顶元素了
        Que.pop();
        return result;
    }
    
    int top() { //取栈顶元素
        return Que.back();  //队尾即为栈顶
    }
    
    bool empty() {  //判断栈空
        return Que.empty();
    }

匹配系列

1036: 括号匹配PIPIOJ

题目描述

PIPI给你一个合法的括号序列,希望跟你按左括号下标递增顺序给出每对括号在序列中的下标。(下标从1开始)

输入:多组数据,请处理到EOF。对于每组数据,第一行包括一个只包含'(',')'的字符串,保证输入的括号匹配序列合法,长度不超过100000

输出:按左括号下标递增顺序给出每对括号在序列中的下标。

样例输入:( ( ) ) ( ) ( )

样例输出

1 4  
2 3  
5 6  
7 8

思路:这道题要求我们返回的是括号的下标,那么我们就要搞清楚一点,我们要把什么压入栈中?是压入括号,还是压入数组下标?当然是下标对吧,不然怎么区分谁是谁的伴侣呢?

所以我们只要遍历一遍字符串数组,遇到左括号将其下标压入栈内,遇到右括号将对应的左括号出栈,同时记录两个括号的下标即可

这道题有个难点,我们怎么个记录法,才能保证最后输出是按照从左到右,左括号的匹配顺序来输出的呢?这里介绍一种原数组标记法,或者采用map集合来标记,不过用数组标记肯定更方便,当然一些科班的同学对这个肯定已经熟悉了,都算不上是难点,哈哈哈哈,但是对于刚开始跨考的我来说可算是个难点呢。

原数组标记法:由于这里的每个括号对应的下标都是不同的,那么我们不妨根据哈希表的映射原理思考下,我们遇到右括号时,将它的下标映射到左括号下标对应的数组位置里面,这样是不是比较好的解决了这个问题,同时能够节省很大的存储空间,毕竟题目给出的数据可是不小呢,如下图,现在8个字符已经造成了4个空间的浪费了。而map的效率远高于数组,查询增删都是O(1),但是考虑到本文的适用性以及初试的要求,还是采用数组叭

代码:

#include<bits/stdc++.h>
using namespace std;
void PareMatch(string s){
    int len=s.size();   //获取串长
    stack<int>st;  
    vector<int>res;
    for(int i=0;i<len;i++){
        if(s[i]=='(') st.push(i+1);    //遇到左括号将下标入栈
        else{   //遇到右括号
            res[st.top()]=i+1;  //哈希数组
            st.pop();   //栈顶出栈
        }
    }
    for(int i=1;i<res.size();i++)    //输出下标
        if(res[i]) printf("%d %d\n",i,res[i]);
}
int main(){
    string s;
    while(cin>>s)
        PareMatch(s);
    return 0;
}

1229: 括号匹配PIPIOJ

题目描述

假设一个算术表达式中包含圆括号,方括号,花括号3种类型的括号,编写算法来判别表达式中括号是否配对。

输入:输入包含多组测试用例。
对于每组测试样例,包括一个仅由 '(',')','[',']','{','}'组成的字符串s。

输出:若s是合法的括号串,输出"yes",否则,输出"no"。

样例输入:1.[ ] ( ( ( ) ) )       2.{ ( ) } ( ]

样例输出:1.yes        2.no

思路:这题也是匹配题,同样用栈来实现,我们遍历一遍字符串数组,遇到左括号则入栈,遇到右括号则出栈进行判断

打磨一下细节:思考一下在这个过程中有哪些可能出现的非法情况呢?

1.首先最容易想到的当然是左右括号不匹配

2.如果遇到右括号时,栈已空,则有多余的右括号

3.如果结束时,栈非空,则有多余的左括号

在代码实现过程中,有个小技巧可以帮助我们,我们在判断栈顶元素的左括号与目前扫描的右括号是否匹配的时候,这段代码稍微有点繁琐,但是不妨逆转下思路,如果我们在遇到左括号时,直接将其对应的右括号入栈,将是否匹配转换为是否相等,是不是一下子就减轻了我们的判断负担?

代码:

#include<bits/stdc++.h>
using namespace std;
bool PareMatch(string s){
    int len=s.size();   //获取串长
    stack<char>st;
    for(int i=0;i<len;i++){
        if(s[i]=='(') st.push(')');
        else if(s[i]=='[') st.push(']');
        else if(s[i]=='{') st.push('}');
        else{   //遇到右括号
            if(st.empty()||st.top()!=s[i]) return false;    //注意先判空
            st.pop();   //匹配则出栈
        }
    }
    if(!st.empty()) return false; //如果有多余的左括号
    return true;
}
int main(){
    string s;
    while(cin>>s){
        if(PareMatch(s)) printf("yes\n");
        else printf("no\n");
    }
    return 0;
}

1260: 相邻相同字母删除PIPIOJ

题目描述

PIPI现在有一段由小写字母组成的文本s,他每天的工作就是找到文本中两个相邻并且相同的字母,然后删除它们。注意这个过程是递归的,比如:
"abbac"->"aac"->"c"。
返回PIPI删除的文本。

输入

输入一行,代表文本s。(1<=|s|<=1e5).

输出

输出一行,代表PIPI经过上述操作之后得到的文本。

思路:这道题就跟我们小时候玩的消消乐差不多吧,用一个栈进行匹配,遍历一遍字符串数组,每遍历个元素就和当前栈顶的元素进行匹配,如果就出栈,最后栈里留下来的就是结果字符串了

代码:

#include<bits/stdc++.h>
using namespace std;
void removeDuplicates(string s){
    int len=s.size();   //获取串长
    stack<char>st;
    for(int i=0;i<len;i++){
        if(st.empty()||st.top()!=s[i]){ //如果当前栈为空或者栈顶元素与当前遍历元素不相同
            st.push(s[i]);
        }
        else st.pop();  //栈顶元素和当前元素相同
    }
    string result="";   //定义一个结果字符串,初始化为空
    while(!st.empty()){
        result+=st.top();   //把栈顶元素加入结果串
        st.pop();
    }
    reverse(result.begin(),result.end());   //逆转下顺序,注意reverse是左闭右开,但是end刚好指向了最后一个字符的后一个位置
    for(int i=0;i<result.size();i++){
        printf("%c",result[i]);
    }
}
int main(){
    string s;
    cin>>s;
    removeDuplicates(s);
    return 0;
}

1360: 删除最外层括号PIPIOJ

题目描述

有效的括号字符串有 () , (A) , A+B, 其中A,B也为有效的括号字符串。若有效的括号字符串S非空,且不存在将S分解为A+B的方法(A,B皆为有效的括号字符串),那么S为不可分解的。给出字符串S,我们将其分解为 S = S1+S2+...+Sn, 其中每一个Si都是不可分解的。
现在要求你将S拆分为n个不可分解的串,并去除掉每一个不可分解串最外层的括号。

输入:输入包含多组测试样例。每一组测试样例都是一个合法字符串S (|S|<100)。

输出:对于每组样例,输出分解之后然后去除掉括号的字符串。

下面这张图来自1021. 删除最外层的括号 - 力扣(LeetCode)

思路:首先明确一点,这道题给出是合法字符串。题目要求我们删除最外层括号,那么我们要做的就是如何区分外层和内层的括号呢?关于匹配问题我们肯定想到的就是用栈来解决,区分方法如下

对于“最外层”的左括号,在它入栈前,栈内一定为空,而对于“最外层”的右括号,栈内一定只有一个与它匹配的左括号,将左括号弹出后,此时就变成了空栈,于是代码判断就浮出水面了,我们只需要将非最外层的括号存入结果字符串即可

代码:

#include<bits/stdc++.h>
using namespace std;
void removeOuterParentheses(string s){
    int len=s.size();   //获取串长
    string result="";   //定义一个结果字符串,初始化为空
    stack<char>st;
    for(int i=0;i<len;i++){
        if(s[i]=='('){
            if(!st.empty()) result.push_back(s[i]);    //内层左括号存入结果串
            st.push(s[i]);   //压入栈中等待匹配
        }
        else{   //遍历到右括号
            st.pop();   //先弹出栈顶左括号再判断
            if(!st.empty()) result.push_back(s[i]);    //内层右括号存入结果串
        }
    }

    for(int i=0;i<result.size();i++){
        printf("%c",result[i]);
    }
    printf("\n");
}
int main(){
    string s;
    while(cin>>s)
        removeOuterParentheses(s);
    return 0;
}

1311: 删除最少无效括号PIPIOJ

题目描述

给你一个仅由 '('、')' 组成的字符串 s。
你需要从字符串中删除最少数目的 '(' 或者 ')' (可以删除任意位置的括号),使得剩下的括号字符串合法。

输入:输入包含一个字符串s , 长度不超过 1e5

输出:输出包含最长的合法字符串。

301. 删除无效的括号 - 力扣(LeetCode)

前言:这道题是真的纸老虎,和LeetCode上的这道题真是很相似,我也是想复杂了,最开始一直没敲出来,LeetCode要运用到DFS+回溯算法进行暴力搜索或者BFS+哈希标记,获取所有的可能,而这道题仅仅只要求一个就行了,千万不要小看这里面的差别。

思路:那么这道题我们要运用到的仅仅只是栈匹配+哈希法标记,依照括号匹配的情况,对所有不合法的括号下标进行标记就好了,所以我们要入栈的也是下标。那么如何判断非法括号呢?

对于右括号很容易想到,只要目前栈内为空,那么该右括号就一定非法

对于左括号的话,所有最终留在栈内的左括号一定非法

 代码:

#include<bits/stdc++.h>
using namespace std;
void removeInvalidParentheses(string s){
    int len=s.size();   //获取串长
    stack<int>st;
    vector<bool>Unlawful(len);
    for(int i=0;i<len;i++){
        if(s[i]=='(') st.push(i);   //下标入栈
        else{   //遇到右括号
            if(st.empty()) Unlawful[i]=1;    //对非法的右括号标记
            else st.pop();
        }
    }
    while(!st.empty()){ //对非法的左括号标记
        Unlawful[st.top()]=1;
        st.pop();
    }

    for(int i=0;i<s.size();i++){
        if(Unlawful[i]==0)printf("%c",s[i]);    //输出合法的括号
    }
}
int main(){
    string s;
    cin>>s;
    removeInvalidParentheses(s);
    return 0;
}

1332: 反转括号之间的子串PIPIOJ

题目描述

PIPI有一个字符串s , 请你按照从括号内到外的顺序,逐层反转每对匹配括号中的字符串。(PS: 串中每个左括号都有相应的右括号进行配对)

输入:输入包含多组测试用例。( ( ( j o ) ) ( p i ) i p )         
第一行输入字符串 s ,s的长度不会超过100。 p i p i o j

输出:对于每组测试用例,输出反转后的字符串(字符串中不包含任何括号)。

思路:本道题要求我们从括号由“内到外”的顺序反转,也就是如下所示

(((jo))(pi)ip)-->((oj)(pi)ip)-->(joipip)-->pipioj

这道题有两种写法

法一:括号匹配+反转

由于这道题的括号一定是匹配的,那么我们只要在遇到右括号的时候将括号内的字符串反转一次就行了,这里要注意,我们遇到右括号的顺序一定是从内到外,从左到右遇到的。那么我们在这个过程中将谁压入栈内,当然是括号的下标,每次遇到右括号就将匹配的左括号的下标取出来,调用reverse反转这个区间内的字符串就行了

代码:

#include<bits/stdc++.h>
using namespace std;
void reverseParentheses(string &s){   //反转括号内的子串
    int len=s.size();
    stack<int>st;
    for(int i=0;i<len;i++){
        if(s[i]=='(') st.push(i);    //左括号的下标入栈
        else if(s[i]==')'){   //遇到右括号
            int left=st.top();st.pop(); //取出匹配的左括号下标
            reverse(s.begin()+left,s.begin()+i+1);  //注意reverse是左闭右开区间
        }
    }
    for(int i=0;i<len;i++){ //将反转后的字符串输出
        if(s[i]!='('&&s[i]!=')')
            printf("%c",s[i]);
    }
    printf("\n");
}
int main(){
    string s;
    while(cin>>s)
        reverseParentheses(s);
    return 0;
}

法二:括号预处理

上述方法由于每次遇到右括号都需要调动reverse()函数,时间复杂度O(n^{2}),而我们再来仔细思考下上个方法的逻辑,遇到右括号就要对括号内的字符串进行反转,用一个最简单的例子(cba)反转后变成了(abc),我们的输出也是abc,那不妨换一种思路:如果我们不进行反转,直接从右往左输出呢,是不是同样可以得到abc,顿时拍案叫绝!

那么此题的关键就在于,我们对这个字符串如何进行遍历,最后能得到我们想要的顺序。思路当然不仅仅只是上面那么简单,这里再详细举例子讲述一下

首先遍历一遍字符串,我们用一个next数组记录每对括号的下标,当然不能记录自己的下标,那样就失去了意义,在这里我们要记录匹配的另一方的下标,也就是“对象”的下标,就好比她爱着你,你爱着她,如下图所示。

得到next数组后,我们遍历数组,每遇到一个括号,我们就跳到其匹配的括号下标处,并改变遍历方向,以简单的(cba)举例,首先从左往右遍历,遇到左括号,跳到右括号并向左遍历,纳入结果“cba”,遇到左括号,跳到对应的右括号处向右遍历,此时遍历结束,大家可以按照这个思路手动模拟下上述图片中是如何进行遍历的

代码:

#include<bits/stdc++.h>
using namespace std;
void GetNext(string s,vector<int>& next,int len){   //获取next数组
    stack<int>st;   //需要一个栈来匹配括号
    for(int i=0;i<len;i++){
        if(s[i]=='(') st.push(i);   //左括号下标入栈
        else if(s[i]==')'){
            int j=st.top(); st.pop();   //取出左括号下标
            next[j]=i;  //互相爱上对方
            next[i]=j;
        }
    }
}
void reverseParentheses(string &s){   //反转括号内的子串
    int len=s.size();
    vector<int>next(len);
    GetNext(s,next,len);    //获取next数组
    int direction=1;    //用于改变方向,1朝右,-1朝左
    string result="";   //初始化结果串
    for(int i=0;i<len;i+=direction){
        if(s[i]=='('||s[i]==')'){
            i=next[i];  //跳到匹配的括号处
            direction= -direction;   //修改遍历方向
        }
        else result+=s[i];
    }

    for(int i=0;i<result.size();i++) //输出结果串
        printf("%c",result[i]);
    printf("\n");
}
int main(){
    string s;
    while(cin>>s)
        reverseParentheses(s);
    return 0;
}

栈在表达式中的运用

1362: 计算表达式PIPIOJ

题目描述

对于一个不存在括号的表达式进行计算(包含加减乘除四种运算符,除法为向下取整)。

输入:多组数据,每组数据占据一行。734/2-56*2-7*8

输出:输出计算结果。199

思路:这道题需要用到几个栈呢?如果只用一个数字栈的话,那么如上例所示,我们在扫描到除号的时候,显然栈内只有一个元素无法运算,所以这道题我们需要用到两个栈,一个数字栈和一个运算符栈。遍历一遍数组,会遇到以下4种情况

1.遇到数字我们先不能着急入栈,因为数字是一个一个扫描的,所以在遇到运算符前,我们只能逐步确定数字到底是多少

2.在运算符栈为空或当前栈顶运算符优先级更高的情况下,扫描到运算符则先将其入栈,因为我们并不确定它的运算顺序

3.在运算符栈不空的情况下,我们需要比较当前运算符的优先运算等级(所以需要一个比较优先运算等级函数),将所有低于或等于当前运算符等级的全部弹出栈来运算(还需要一个运算函数),再将当前运算符入栈

4.遇到输入只有"3+4"的情况,这里不注意的话遍历完一遍数组并不会进行任何运算操作,所以这里有两种处理办法:第一种是在for循环结束后我们对运算符栈进行判空,如果非空则调动运算符函数。第二种是我们一开始在字符串最后末尾处增加一个'+"或‘-’运算符,这样只要遍历到最后一个运算符,我们必然要对运算符栈进行清空操作。这里我采用了第二种

代码:

#include<bits/stdc++.h>
using namespace std;
int priority(char s){   //获取运算符的优先级
    if(s=='*'||s=='/') return 1;
    else return 0;  //+或-
}
int OP(char x,int a,int b){  //运算函数
    if(x=='+') return a+b;
    else if(x=='-') return a-b;
    else if(x=='*') return a*b;
    else return a/b;
}
void ComputationExpression(string s){   //计算表达式
    int len=s.size();   //获取串长
    s.push_back('+');   //
    stack<char>op;  //运算符栈
    stack<int>num;  //数字栈
    int ans=0;  //用来逐步确定数字
    for(int i=0;i<len+1;i++){   //len要加1,注意我们多增加了一个字符
        if(s[i]>='0'&&s[i]<='9')   //遇到数字
            ans=ans*10+s[i]-'0';    //将char转换为int数字
        else{   //遇到运算符
            num.push(ans);  //先将得到的数字压入栈中
            ans=0;  //便于下次计数
            if(op.empty()||priority(op.top())<priority(s[i]))  //当前栈为空或栈顶运算符等级更低
                op.push(s[i]);
            else{
                while(!op.empty()&&priority(op.top())>=priority(s[i])){ //一直出栈进行运算,直到栈空或者当前栈顶运算符等级更低
                    int a=num.top(); num.pop(); //取栈顶元素并出栈
                    int b=num.top(); num.pop();
                    char x=op.top(); op.pop();
                    num.push(OP(x,b,a));  //注意这里要反过来,因为栈输出是逆序的
                }
                op.push(s[i]);  //将当前遍历的运算符入栈
            }
        }
    }
    printf("%d\n",num.top());
}
int main(){
    string s;
    while(cin>>s)
        ComputationExpression(s);
    return 0;
}

1291: 中缀表达式转后缀表达式I(不带括号)PIPIOJ

题目描述

PIPI现在有若干个包含小写英文字母作为操作数 以及 '+', '-', '*', '/', '^' 五种操作符的合法中缀表达式。请你将其转为后缀表达式(逆波兰式)。

输入:输入一个字符串s,表示中缀表达式。        a^b+c*d/e-f

输出:输出一个字符串表示对应的后缀表达式。        ab^cd*e/+f-

思路:对于中缀转后缀,我们需要从左往右遍历字符串,这与我们手工转换的思想一致(注意这里是不带括号类型的表达式,下道题我们给出带括号类型的),而这道题的题解思路其实和上道题类似,因为计算机的计算方法就是靠逆波兰表达式来运算的,只不过我们上题实现了一遍这个功能而已,但区别在于此题我们只需要一个栈即可,因为我们不需要进行运算存储结果

在遍历过程中我们会遇到以下4种情况

1.遇到字母时,我们直接输出

2.遇到运算符时,如果当前运算符栈为空或者栈顶运算符的运算等级低于当前运算符,我们直接入栈

3.遇到运算符时,如果当前栈顶运算等级高于或等于当前运算符,则一直输出,直到满足第2种情况

4.最后我们依然需要对"a+b"这种特殊情况进行处理

代码:

#include<bits/stdc++.h>
using namespace std;
int priority(char s){   //获取运算符的优先级
    if(s=='^') return 2;
    else if(s=='*'||s=='/') return 1;
    else return 0;  //+或-
}
void Infixturnsuffix(string s){   //中缀转后缀
    int len=s.size();   //获取串长
    s.push_back('+');   //
    stack<char>op;  //运算符栈
    for(int i=0;i<len+1;i++){   //len要加1,注意我们多增加了一个字符
        if(s[i]>='a'&&s[i]<='z')   //遇到字母,直接输出
            printf("%c",s[i]);
        else{   //遇到运算符
            if(op.empty()||priority(op.top())<priority(s[i]))  //当前栈为空或栈顶运算符等级更高
                op.push(s[i]);
            else{
                //一直出栈进行输出,直到栈空或者栈顶运算符等级小于当前运算符
                while(!op.empty()&&priority(op.top())>=priority(s[i])){
                    printf("%c",op.top());
                    op.pop();
                }
                op.push(s[i]);
            }
        }
    }
}
int main(){
    string s;
    cin>>s;
    Infixturnsuffix(s);
    return 0;
}

1292: 中缀表达式转后缀表达式II(带括号)PIPIOJ

题目描述

PIPI现在有若干个包含小写英文字母作为操作数 以及 '+', '-', '*', '/', '^' ,'(',')'七种操作符的合法中缀表达式。请你将其转为后缀表达式(逆波兰式)。
PS:'^'代表幂运算,2^3=8

输入:输入一个字符串s,表示中缀表达式。a+b*c+(d*e+f)*g

输出:输出一个字符串表示对应的后缀表达式。abc*+de*f+g*+

思路:上一道题是此题的简化版本,我给出大家一个手工模拟的过程,可以自行体会下,此题代码的主要区别就在于我们如何处理左右括号,不难知道,括号内的一定要最先运算,那么左括号的等级应该是最低的,只有右括号才能让它出栈,简单谈一下遇到括号如何处理

1.当遇到左括号时,由于它的运算符等级最低,那么此时栈内如果有运算符必然会先出栈,而我们知道,前面的计算表达式其实是隐含了括号运算的,所以我们这里要对左括号进行特殊处理,直接入栈

2.遇到右括号时,一直出栈输出,知道遇到左括号

 代码:

#include<bits/stdc++.h>
using namespace std;
int priority(char s){   //获取运算符的优先级
    if(s=='(') return 0;
    else if(s=='^') return 3;
    else if(s=='*'||s=='/') return 2;
    else return 1;  //+或-
}
void Infixturnsuffix(string s){   //中缀转后缀
    int len=s.size();   //获取串长
    s.push_back('+');   //
    stack<char>op;  //运算符栈
    for(int i=0;i<len+1;i++){   //len要加1,注意我们多增加了一个字符
        if(s[i]>='a'&&s[i]<='z')   //遇到字母,直接输出
            printf("%c",s[i]);
        else{   //遇到运算符
            if(s[i]==')'){  //遇到右括号,清空一波括号内的运算符
                while(op.top()!='('){
                    printf("%c",op.top()); op.pop();    //输出并出栈
                }
                op.pop();   //左括号出栈
            }
            else if(s[i]=='(') op.push(s[i]);    //左括号直接入栈
            else if(op.empty()||priority(op.top())<priority(s[i]))  //当前栈为空或栈顶运算符等级更高
                op.push(s[i]);
            else{
                //一直出栈进行输出,直到栈空或者栈顶运算符等级小于当前运算符
                while(!op.empty()&&priority(op.top())>=priority(s[i])){
                    printf("%c",op.top());
                    op.pop();
                }
                op.push(s[i]);
            }
        }
    }
    printf("\n");
}
int main(){
    string s;
    cin>>s;
    Infixturnsuffix(s);
    return 0;
}

总结

本章主要介绍了匹配系列中栈是如何大显身手的,以及如何计算表达式,技巧上倒是没有什么可以说的,思想都大同小异,关键在于我们要知道要把“谁“”压入栈,有的时候是压入下标,有的时候是压入字符,有的时候还需要两个栈来匹配,明确了这一点,问题也就迎刃而解了。

在后续还会介绍到栈是如何在迭代中大放光彩的,也就是二叉树的算法里面。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值