关于后缀/中缀/前缀表达式的小结
这个表达式计算是我好早之前学的 但是昨天听完 我一瞬间竟然忘了这东西怎么写 又翻了翻代码 想起来了 不如写下来
为什么会出现前缀 后缀 中缀的形式 是因为 我们在计算表达式的时候 实际上可以看做是一个插入一棵树上 然后对应这个树上的前序 后序 中序的一个遍历顺序
那么不同的 遍历顺序 表达式计算方式也会有所不同 我们这里讲一下 前后缀表达式的计算 以及如何将中缀转化为后缀 在进行计算 这是一般做法
应该都知道 表达式计算都要搞一个栈 别说了 我不信你不知道;
介绍概念 中缀表达式(中缀记法)我们最常写的式子 中缀表达式是一种通用的算术或逻辑公式表示方法,
操作符以中缀形式处于操作数的中间。
虽然人的大脑很容易理解与分析中缀表达式,但对计算机来说中缀表达式却是很复杂的,
因此计算表达式的值时,通常需要先将中缀表达式转换为前缀或后缀表达式,
然后再进行求值。对计算机来说,计算前缀或后缀表达式的值非常简单。
前缀表达式(前缀记法、波兰式)前缀表达式的运算符位于操作数之前。
前缀表达式的计算机求值:
从右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 op 次顶元素),并将结果入栈;
重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果。
例如前缀表达式“- × + 3 4 5 6”:
(1) 从右至左扫描,将6 5 4 3压入堆栈;
(2) 遇到+运算符,因此弹出3和4(注意栈的应用 此时3 4是栈顶),计算出3+4的值,得7,再将7入栈;
(3) 接下来是×运算符,因此弹出7和5,计算出7×5=35,将35入栈;
(4) 最后是-运算符,计算出35-6的值,即29,由此得出最终结果。
然后我们考虑 后缀表达式的计算
与上面稍有不同 我们从左向右依次便利这个表达式 遇到是数字就进栈 遇到是符号 就将处于栈顶两个数字出栈 进行运算
运算结果进栈 一直到最终获得结果
后缀表达式也叫做 逆波兰表达式 这样的式子的特点 是没有 括号的 所以我们直接模拟就可以了
但是我们在这里要讨论一下 中缀表达式的计算
也就是 我们最常写的式子 但是 计算起来 我们有两个方法 将其转化成 前缀表达式 或者转化成 后缀表达式 在按照上面的 方式 计算 这里说一
下 转化为后缀表达式
先说一下 怎么转化 这里我们假定 只有小括号 只有整数 输入合法 例如:
中缀表达式为:1+(2-3)*4+4/2
对应后缀表达式为:1 2 3 - 4* + 4 2 / +
如何将一个中缀表达式转化为后缀表达式?我们需要借助栈,用它来存放运算符。
首先将各种运算符(包括括号)的优先级排列如下(数字越大,优先级越高):
1: ( 要注意的是 小括号优先级是最低的qwq
2:+ -
3:* /
4 :^
5:)
这里只考虑到有乘方的形式
对输入的中缀表达式从左到右遍历:
1)如果遇到数字,直接添加到后缀表达式末尾;
2)如果遇到运算符+、-、*、/:
先判断栈是否为空。若是,则直接将此运算符压入栈。若不是,则查看当前栈顶元素。若栈顶元素优先级大于或等于此操作符级别,则弹出
栈顶元素,将栈顶元素添加到后缀表达式中,并继续进行上述判断。如果不满足上述判断或者栈为空,将这个运算符入栈。要注意的是,经
过上述步骤,这个运算符最终一定会入栈。
3)如果遇到括号:如果是左括号,直接入栈。如果是右括号,弹出栈中第一个左括号前所有的操作符,并将左括号弹出。
4)字符串遍历结束后,如果栈不为空,则弹出栈中所有元素,将它们添加到后缀表达式的末尾,直到栈为空。
通过上述过程 我们就得到了 这个中缀表达式 对应的后缀表达式 那么 我们怎么计算后缀表达式也清楚了 但是这种发现有点 麻烦 我们还要记
录这个后缀表达式是什么
对于一些题目 不需要你输出 后缀表达式 那我们考虑直接计算 不记录这个后缀表达式 直接将两步结合起来;
我们考虑开两个栈 一个数字栈 一个符号栈 我们读入当前的中缀表达式 遇到数字 我们将其压入数字栈 遇到符号 按照刚才的方式处理
对符号栈进行处理 对应优先级的顺序 也就是说 对于一个操作符 我们将优先级大于等于当前符号的 都计算出来
方法类似于后缀表达式 一个符号 两个数字 计算结果压入栈中;
所以我们无需计算出具体的后缀表达式 直接计算即可;
鉴于写文章的作者比较懒 所以以上都没有代码 不
过luogu上倒是有后缀表达式 以及简单的中缀表达式计算的例题 如1449 但为啥都是普及-啊
所以 只有计算中缀的一个题目 涉及 负数 多位数字的处理 以及加 减 乘 整除 乘方 括号 多种运算
给出一个表达式,其中运算符仅包含+,-,*,/,^
(加 减 乘 整除 乘方)要求求出表达式的最终值。
数据可能会出现括号情况,还有可能出现多余括号情况。
数据保证不会出现大于或等于231的答案。
数据可能会出现负数情况。
输入格式
输入仅一行,即为表达式。
输出格式
输出仅一行,既为表达式算出的结果。
输入样例:
(2+2)^(1+1)
输出样例:
16
我们按照上面的思路来一遍就好了 注意好好处理数字
#include<bits/stdc++.h> using namespace std; stack<int> num; stack<char> ops; inline int mul(int a,int k) {//慢速幂??? int res=1; while(k--) res*=a; return res; } void cal() {//计算表达式的值 按照后缀表达式的方式 int num1,num2,num3; num1=num.top(); num.pop(); num2=num.top(); num.pop(); char op=ops.top(); ops.pop(); if(op=='+') num3=num2+num1; else if (op=='-') num3=num2-num1; else if (op=='*') num3=num2*num1; else if (op=='/') num3=num2/num1; else num3=mul(num2,num1); num.push(num3);//计算结果压入栈中 } int main() { string str; cin >> str; string left; for(int i=0;i<str.size();i++) left+='('; str=left+str+')';//处理多余括号的情况 可以多左括号 但是多右括号就麻烦了 for(int i=0;i<str.size();i++) { if(str[i]>='0'&&str[i]<='9') { int j=i,t=0; while(str[j]>='0'&&str[j]<='9') { t=t*10+str[j]-'0'; j++; } num.push(t);//处理多位数字的情况 题目并没有保证是10以内的数字 i=j-1; } else { char c=str[i]; if(c=='(') ops.push(c);//左括号 直接入栈 else if(c=='+'||c=='-') { if(c=='-'&&i&&!(str[i-1]>='0'&&str[i-1]<='9')&&str[i-1]!=')') {//处理负数的情况 将减去一个数 改成+负数 int j=i+1,t=0; while(str[j]>='0'&&str[j]<='9') { t=t*10+str[j]-'0'; j++; } num.push(-t); i=j-1; } else { while(ops.top()!='(') cal(); ops.push(c); } } else if(c=='*'||c=='/') { while(ops.top()=='*'||ops.top()=='/'||ops.top()=='^') cal();//优先级大于等于加减的计算完 ops.push(c); } else if(c=='^') { while(ops.top()=='^') cal();//乘方的优先级最高 ops.push(c); } else if(c==')') { while(ops.top()!='(') cal();//遇到右括号 ops.pop(); } else cout<<"sbsbsbsb"<<endl;//无解????? } } cout<<num.top()<<endl; return 0; }