一、后缀表达式
1、理解
对于算术表达式来说,其基本规则是:先乘除,后加减;先括号内,再括号外;相同优先级情况下从左到右。比如,5 + 6 / 2 - 3 * 4 就是一个算术表达式,它的正确理解应该是:5 + 6 / 2 - 3 * 4 = 5 + 3 - 3 * 4 = 8 - 12 = -4。可以看出这类表达式主要由两类对象构成的,即运算数和运算符号。
我们先来分析一类仅由两种运算符号和三个运算数构成的相对简单的算术表达式,比如2 + 3 * 4或2 * 3 + 4,其基本形式是a1 op1a2op2a3,其中ai为运算数,opi为运算符号。
当计算机编译程序分析这样的表达式,一般就是从左到右扫描。当扫描了a1op1a2找到两个运算数一个运算符号后,并设法做出是否马上进行运算的决定。因为还需要看op2的优先级是否比op1高。所以,编译程序从左往右扫描时,需要根据前后运算优先级的情况决定;先保留当前数据和运算符号,还是马上进行计算。比如,对于2 + 3 * 4,当扫描了2 + 3后还不能做出计算2 + 3的决定,因为后面" * "的优先级比" + "高,所以必须先将2 、3以及" + "保留起来,等到后面合适机会时再将这些数据和运算符号拿来运算。所以,需要有一种数据结构能够很好地实现对等待运算或数据的组织。
为了更容易理解表达式的求值方法,我们分析一下更简单的一种表达式:后缀表达式。平常我们经常使用的表达式是中缀表达式,即运算符号位于两个运算数之间的表达式。而在后缀表达式中,运算符号位于两个运算数之后。比如,前面提到常量表达式5 + 6 / 2 - 3 * 4的后缀形式就是:5 6 2 / + 3 4 * - 。还有一种表达式形式叫前缀表达式,运算符号位于两个运算数之前。比如,5 + 6 / 2 - 3 * 4的前缀形式就是:- + 5 / 6 2 * 3 4。
可见,后缀表达式运算数出现的顺序与相应中缀表达式一样,但运算符出现在不同的位置。
后缀表达式相对比中缀表达式的求值要容易得多。我们先来看一下,后缀表达式5 6 2 / + 3 4 * -如何求解。
同样,我们还是从左到右扫描这个表达式,求解过程如下。
(1)遇见运算数5 6 2时均不做计算,同时记住这个序列5 6 2。
(2)当遇见运算符号" / "时,把最近遇到的两个数6和2从序列中取出作运算,并把结果3放到刚才那个序列的后面,即当前序列为:5 3。
(3)当遇见运算符号" + "时,把序列的最后两个数 5 和 3 取出作运算,并把结果8放到当前的序列的后面,因而当前序列为:8。
(4)遇见 3 4 时均不做计算,把这两个数放到当前序列的后面,因为当前序列为8 3 4.
(5)当遇见运算符号" * "时,把当前序列的最后两个数 3 和 4 取出作运算,并把结果12放到当前序列的后面,因而当前序列为:8 12。
(6)当遇见运算符号" - "时,把当前的最后两个数 8 和 12 取出作运算,并把结果 -4 放到当前序列的后面,因而当前序列为:-4。
(7)当输入中不再有符号时,当前序列中的值(-4)就是表达式的结果值。
2、题目
题目1:(后缀表达式)
题目描述
所谓后缀表达式是指这样的一个表达式:式中不再引用括号,运算符号放在两个运算对象之后,所有计算按运算符号出现的顺序,严格地由左而右新进行(不用考虑运算符的优先级)。
如:3*(5–2)+7对应的后缀表达式为:3.5.2.-*7.+@。’@’为表达式的结束符号。‘.’为操作数的结束符号。
输入格式:
后缀表达式
输出格式:
表达式的值
输入输出样例
输入:
3.5.2.-*7.+@
输出:
16
说明提示:
字符串长度,1000内。
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3 + 10;
ll stk[N];
int main()
{
ll top = 0, now = 0;
char op;
while((op = getchar()) != '@')
{
// 数字
if(op >= '0' && op <= '9')
{
now *= 10;
now += op - '0';
}
else if(op == '.')
{
stk[++ top] = now;
now = 0;
}
else if(op == '+')
{
stk[top - 1] += stk[top];
stk[top] = 0;
top --;
}
else if(op == '-')
{
stk[top - 1] -= stk[top];
stk[top] = 0;
top --;
}
else if(op == '*')
{
stk[top - 1] *= stk[top];
stk[top] = 0;
top --;
}
else if(op == '/')
{
stk[top - 1] /= stk[top];
stk[top] = 0;
top --;
}
}
printf("%lld",stk[1]);
return 0;
}
题目2:(表达式的转换:中缀转后缀)
题目描述:
平常我们书写的表达式称为中缀表达式,因为它将运算符放在两个操作数中间,许多情况下为了确定运算顺序,括号是不可少的,而中缀表达式就不必用括号了。
后缀标记法:书写表达式时采用运算紧跟在两个操作数之后,从而实现了无括号处理和优先级处理,使计算机的处理规则简化为:从左到右顺序完成计算,并用结果取而代之。
例如:8-(3+2*6)/5+4
可以写为:8 3 2 6*+5/-4+
其计算步骤为:
8 3 2 6 * + 5 / – 4 +
8 3 12 + 5 / – 4 +
8 15 5 / – 4 +
8 3 – 4 +
5 4 +
9
编写一个程序,完成这个转换,要求输出的每一个数据间都留一个空格。
输入格式:
就一行,是一个中缀表达式。输入的符号中只有这些基本符号0123456789+-*/^()
,并且不会出现形如2*-3
的格式。
表达式中的基本数字也都是一位的,不会出现形如12
形式的数字。
所输入的字符串不要判错。
输出格式:
若干个后缀表达式,第I+1
行比第I
行少一个运算符和一个操作数,最后一行只有一个数字,表示运算结果。
样例:
输入:
8-(3+2*6)/5+4
输出:
8 3 2 6 * + 5 / - 4 + 8 3 12 + 5 / - 4 + 8 15 5 / - 4 + 8 3 - 4 + 5 4 + 9
说明/提示:
运算的结果可能为负数,/
以整除运算。并且中间每一步都不会超过2 ^ 31。字符串长度不超过100。
思路:
这题最麻烦的就是把中缀表达式转化成后缀表达式,难点是处理优先级的问题。 我们从左往右扫描中缀表达式,进行如下处理:
1.
建立一个符号栈op和一个存后缀表达式的字符串p。
2.
如果是数字则加入p,因为形为"A op B"的中缀表达式转成后缀表达式就成了"A B op" , 数字一定在前。
3.
如果是左括号,则加入符号栈op。
4.
如果是右括号,则把符号栈中的运算符弹到s,直到遇到最近的左括号。最后,把一个左括号删除(不入p)。
5.
如果是运算符,则把优先级大于或等于的栈顶符号弹入p。因为这样能保证优先级高的先算。
为什么,优先级等于也要弹出呢?因为同级运算从左往右啊。
6.
重复2~5,直到扫描完毕。
7.
把剩下的符号依次弹入p。
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3 + 10;
//思路:把中缀表达式转成后缀表达式,再对后缀表达式进行处理。
//每处理一次运算,把数值栈中的数字输出,并把未处理的符号、数字输出。
char s[N];//中缀表达式
ll val[N];int top;//数值栈
stack<char>op;//符号栈
char p[N];int p_top;//postfix Expression 后缀表达式
int grade(char op)//优先级
{
if(op == '+' || op == '-') return 1; // +,-优先级最低,用1表示
if(op == '*' || op == '/') return 2; // 其次是×,/,用2表示
if(op == '^') return 3; // ^优先级最高,用3表示
else return 0; // 此处可不写,防止报错
}
void calc(char op)//计算
{
ll x = val[top --], y = val[top];
switch(op)
{
case '+':
y+=x;
break;
case '-':
y-=x;
break;
case '*':
y*=x;
break;
case '/':
y/=x;
break;
case '^':
y=pow(y,x);
}
val[top]=y;
}
int main()
{
scanf("%s",s);
int n = strlen(s);
for(int i = 0 ; i < n ; i ++)
{
if(s[i] >= '0' && s[i] <= '9') p[++ p_top] = s[i]; //数字直接加入后缀表达式,因为形为"A op B"的中缀表达式转成后缀表达式就成了"A B op" , 数字一定在前
else
{
//括号
if(s[i] == '(' ) op.push(s[i]); // 左括号加入符号栈
else if(s[i] == ')') // 如果是右括号
{
while(!op.empty() && (op.top()) != '(' ) // 符号栈不空并且符号栈的栈顶元素不为左括号
{
p[++ p_top] = op.top(); //
op.pop(); // 弹出符号栈的栈顶元素
}
op.pop(); // 弹出符号栈的左括号
}
else//运算符
{
while(!op.empty() && grade(op.top()) >= grade(s[i])) // 符号栈不空当前符号栈的栈顶元素的优先级大于等于当前枚举的元素
{
p[++ p_top] = op.top(); // 符号栈的栈顶元素加入后缀表达式
op.pop(); // 弹出符号栈的栈顶元素
}
op.push(s[i]); // 将该运算符加入符号栈
}
}
}
while(!op.empty()) // 当符号栈不为空的时候
{
p[++ p_top] = op.top(); // 将符号栈的栈顶元素加入后缀表达式
op.pop(); // 弹出符号栈的栈顶元素
}
for(int i = 1 ; i <= p_top ; i ++) printf("%c ",p[i]); // 输出每个阶段的后缀表达式
puts("");//输出中缀表达式
for(int i = 1 ; i <= p_top ; i ++) //逐步处理后缀表达式
{
if(p[i] >= '0' && p[i] <= '9') val[++ top] = p[i] - '0'; //
else//运算符
{
calc(p[i]);
for(int j = 1 ; j <= top ; j ++) printf("%lld ",val[j]);
for(int j = i + 1 ; j <= p_top ; j ++) printf("%c ",p[j]);
puts("");
}
}
return 0;
}