在本周作业中遇到了后缀表达式(逆波兰式)。一是将一般的中缀表达式转化为后缀表达式;二是后缀表达式的值的计算。
这两题我都用的是数组模拟栈来解决,查阅资料并不多,百度百科:逆波兰式 中的逆波兰式的概念、实现、举例我觉得都比csdn、博客园上的帖子详细而且好理解,所以主要是参考了百度百科。
题一:将中缀表达式转化为后缀表达式
所谓后缀表达式是指这样的一个表达式:式中不再引用括号,运算符号放在两个运算对象之后,所有计算按运算符号出现的顺序,严格地由左而右进行(不用考虑运算符的优先级)。
如:中缀表达式 3(5–2)+7 对应的后缀表达式为:352-7+ 。
请将给出的中缀表达式转化为后缀表达式并输出。
输入格式:
输入仅一行为中缀表达式,式中所有数字均为个位数,表达式长度小于1000。
输出格式:
输出一行,为后缀表达式,式中无空格。
输入样例:
2+4*8+(8 * 8+1)/3
输出样例:
248*+88 * 1+3/+
一般算法:
首先需要分配2个栈,一个作为临时存储运算符的栈S1(含一个结束符号),一个作为输入逆波兰式的栈S2(空栈),S1栈可先放入优先级最低的运算符#,即s1[0]='#'。从中缀式的左端开始取字符,逐序进行如下步骤:
(1)若取出的字符是操作数,则分析出完整的运算数(数字),该操作数直接送入S2栈。本题的要求较为简单,题面说数字只有个位数,所以不存在10以上的数字。
(2)若取出的字符是运算符,则将该运算符与S1栈栈顶元素比较,如果该运算符优先级(不包括括号运算符)大于S1栈栈顶运算符优先级,则将该运算符进S1栈,否则,将S1栈的栈顶运算符弹出,送入S2栈中,直至S1栈栈顶运算符低于(不包括等于)该运算符优先级,最后将该运算符送入S1栈。
(3)若取出的字符是“(”,则直接送入S1栈顶。
(4)若取出的字符是“)”,则将距离S1栈栈顶最近的“(”之间的运算符,逐个出栈,依次送入S2栈,此时抛弃“(”。
(5)重复上面的1~4步,直至处理完所有的输入字符
(6)若取出的字符是“#”,则将S1栈内所有运算符(不包括“#”),逐个出栈,依次送入S2栈。
(7)从0到S2的栈依次输出S2的字符,即为答案。
编写一个判断字符优先级的函数:
int oplevel(char op)
{
if(op=='*'||op=='/')return 3;
if(op=='+'||op=='-')return 2;
if(op=='(')return 1;
if(op=='#')return 0;
}
本题除了加减乘除外还有涉及到左括号( 的判断和最低级的 # 的判断。
读入、处理:
s1[0]='#';
string str;
int cur1=1,cur2=0;//两个栈的栈顶
cin>>str;
之后op=str[i],再进行如下处理:
如果op是数字,压入S2,如果是左括号,直接压入S1:
if(op>='0'&&op<='9')s2[cur2++]=op;
if(op=='(')s1[cur1++]=op;
遇到右括号):
if(op==')')
{
while(1)
{
if(s1[cur1-1]=='('){
s1[cur1-1]=0;
cur1--;
break;
}
s2[cur2++]=s1[cur1-1];
cur1--;
}
}
如果是加减乘除的话:
if(op=='+'||op=='-'||op=='*'||op=='/')
{
while(1)
{
if(oplevel(s1[cur1-1])<oplevel(op)) //如果当前S1的栈顶比op的优先级低,则将op压入S1
{
s1[cur1++]=op;
break;
}
else
//否则不断将S1栈的栈顶运算符弹出,送入S2栈中,直至S1栈栈顶运算符低于(不包括等于)op的优先级,最后将op送入S1栈
{
s2[cur2++]=s1[cur1-1];
s1[cur1-1]=0;
cur1--;
}
}
}
最后将S1中剩下的元素压入S2:
for(int i=cur1-1;i>0;i--)s2[cur2++]=s1[i];
输出答案:
for(int i=0;i<cur2;i++)cout<<s2[i];
题二:计算后缀表达式的值
Kunkun学长觉得应该让学弟学妹了解一下这个知识点:后缀表达式相对于中缀表达式更容易让计算机理解和学习。现在kunkun学长给出一串后缀表达式,你能帮他算出这个后缀表达式的值吗?
输入格式:
第一行输入后缀表达式长度n(1<=n<=25000);
第二行输入一个字符串表示后缀表达式(每个数据或者符号之间用逗号隔开,保证输入的后缀表达式合法,每个数包括中间结果保证不超过long long长整型范围)
输出格式:
输出一个整数,即后缀表达式的值。
输入样例1:
6
10,2,+
输出样例1:
12
输入样例2:
14
2,10,2,+,6,/,-
输出样例2:
0
一般算法:
首先需要建立一个栈stk用来储存数值和用于计算。从后缀表达式的左端开始取字符:
(1)若取出的字符是数字,则分析出完整的数字后放入栈顶。
(2)若取出的字符是加减乘除就对栈顶下一个数stk[cur-2]与栈顶stk[cur-1]做运算,将结果赋值给stk[cur-2],并将栈顶减一。
(3)分析完所有字符后,输出栈底stk[0],即为答案。
定义以及处理:
long long stk[100000];//全局变量
其他变量:
long long n,now=0,cur=0;
string s;
char op,last;
bool nega=false;//negative 负数
cin>>n>>s;
判断输入的数字是否为负数:
if(op=='-'&&s[i+1]>='0'&&s[i+1]<='9')//当前是负号 - 且下一个字符是数字,则这个输入的是负数而不是减号
{
nega=true;
continue;
}
提取数字:
if(op==','&&s[i-1]>='0'&&s[i-1]<='9')//如果逗号前是数字
{
if(nega){
now*=-1;
nega=false;
}
stk[cur++]=now;
now=0;
}
遇到加减乘除,做运算。
这步是该题的重点!!要搞清楚cur所指向的是栈顶元素的上面一格,另外在减 和 除 运算中,栈顶元素下面一格stk[cur-2]为被减数和被除数,需要注意。同时要赋值给的是stk[cur-2]。
if(op=='+'||op=='-'||op=='*'||op=='/')
{
long long a=stk[cur-1],b=stk[cur-2];
if(op=='+')stk[cur-2]=b+a;
if(op=='-')stk[cur-2]=b-a;//注意是谁减谁
if(op=='*')stk[cur-2]=b*a;
if(op=='/')stk[cur-2]=b/a;//谁除谁
cur--;
}
最后输出栈底即为答案。