洛谷-P1175 表达式的转换
题目
Problem Description
平常我们书写的表达式称为中缀表达式,因为它将运算符放在两个操作数中间,许多情况下为了确定运算顺序,括号是不可少的,而中缀表达式就不必用括号了。
后缀标记法:书写表达式时采用运算紧跟在两个操作数之后,从而实现了无括号处理和优先级处理,使计算机的处理规则简化为:从左到右顺序完成计算,并用结果取而代之。
例如: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
编写一个程序,完成这个转换,要求输出的每一个数据间都留一个空格。
Input
就一行,是一个中缀表达式。输入的符号中只有这些基本符号“0123456789+-*/^()”,并且不会出现形如2*-3的格式。
表达式中的基本数字也都是一位的,不会出现形如12形式的数字。
所输入的字符串不要判错。
Output
Sample Input
8–(3+2*6)/5+4
Sample Output
8 3 2 6 * + 5 / – 4 + 8 3 12 + 5 / – 4 + 8 15 5 / – 4 + 8 3 – 4 + 5 4 + 9
题解
本题涉及到前缀、中缀、后缀表达式的转换,部分参考资料来自百度百科……
中缀表达式(中缀记法)
中缀表达式(或中缀记法)是一个通用的算术或逻辑公式表示方法, 操作符是以中缀形式处于操作数的中间(例:3 + 4),中缀表达式是人们常用的算术表示方法。与前缀表达式(例:+ 3 4)或后缀表达式(例:3 4 +)相比,中缀表达式不容易被计算机解析,但仍被许多程序语言使用,因为它符合人们的普遍用法。与前缀或后缀记法不同的是,中缀记法中括号是必需的。计算过程中必须用括号将操作符和对应的操作数括起来,用于指示运算的次序。
前缀表达式(前缀记法、波兰式)
前缀表达式是一种没有括号的算术表达式,与中缀表达式不同的是,其将运算符写在前面,操作数写在后面。为纪念其发明者波兰数学家Jan Lukasiewicz,前缀表达式也称为“波兰式”。例如,- 1 + 2 3,它等价于1-(2+3)。
后缀表达式(后缀记法、逆波兰式)
后缀表达式,指的是不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则)。
后缀表达式的转换
该题的第一个步骤就是将一个中缀表达式转换为后缀表达式,可以按以下步骤来进行,但是如果有负数存在的话,可能有一点小小的改动。
(1)init两个栈:运算符栈Op和储存过程结果的栈R;
(2)一个一个字符的遍历输入的表达式,直到遍历到表达式的最右边;
2.1)如果遇到数,直接入栈R,此时注意负数的判断;
2.2)如果遇到运算符,比较其与运算符栈Op栈顶运算符的优先级:
如果运算符栈Op为空,或运算符栈Op栈顶运算符为左括号'(',或该运算符的优先级比栈顶运算符的高,则直接将此运算符入栈;
否则,将运算符栈Op栈顶的运算符弹出并压入到栈R中,再次比较其与运算符栈Op栈顶运算符的优先级;
2.3) 如果遇到括号:
如果遇到左括号'(',则直接压入运算符栈Op;
如果遇到右括号')',则依次弹出运算符栈Op栈顶的运算符,并压入栈R,直到遇到左括号为止,并丢弃这对括号;
(3)将运算符栈Op中剩余的运算符依次弹出并压入栈R;
(4)依次弹出栈R中的元素并逆序输出。
例如,将输入样例中的中缀表达式8–(3+2*6)/5+4'转换为后缀表达式的过程如下:
遍历时的元素 | 运算符栈Op(top->bottom) | 栈R(top->bottom) | 步骤 |
8 | empty | 8 | 2.1 |
- | - | 8 | 2.2 |
( | ( - | 8 | 2.3 |
3 | ( - | 3 8 | 2.1 |
+ | + ( - | 3 8 | 2.2 |
2 | + ( - | 2 3 8 | 2.1 |
* | * + ( - | 2 3 8 | 2.2 *优先级比+高 |
6 | * + ( - | 6 2 3 8 | 2.1 |
) | - | + * 6 2 3 8 | 2.3 依次弹出运算符栈Op栈顶的运算符 |
/ | / - | + * 6 2 3 8 | 2.2 /优先级比-高 |
5 | / - | 5 + * 6 2 3 8 | 2.1 |
+ | + | - / 5 + * 6 2 3 8 | 2.2 +优先级比/低,不比-高,栈空 |
4 | + | 4 - / 5 + * 6 2 3 8 | 2.1 |
\0 | empty | + 4 - / 5 + * 6 2 3 8 | 3 |
最后的输出为'8 3 2 6 * + 5 / - 4 +'(注意需要逆序输出)。
后缀表达式的求值
该题的第二个步骤就是逐步计算一个后缀表达式,可以按以下步骤来进行:
(1)init两个栈:储存数据的栈Op(名字不太好,但可以用上面步骤的栈)和储存过程结果的栈R;
(2)从Op弹出元素,直到Op为空;
2.1)如果遇到数,直接入栈R;
2.2)如果遇到运算符,用运算符对栈顶元素和次顶元素做相应的计算(次顶元素 运算符 栈顶元素),然后输出:
栈Op(top->bottom) | 栈R(top->bottom) |
8 3 2 6 * + 5 / - 4 + | empty |
* + 5 / - 4 + | 6 2 3 8 |
+ 5 / - 4 + | 12 3 8 |
5 / - 4 + | 15 8 |
/ - 4 + | 5 15 8 |
- 4 + | 3 8 |
4 + | 5 |
+ | 4 5 |
empty | 9 |
了解了相关知识,用栈就很好实现了,我认真写了一个栈……还是为了数据结构的考试……
代码
#include <iostream> #include <stdlib.h> #include <string.h> #include <cmath> #include <map> #define STACK_SIZE 100 #define STACKINCREMENT 10 #define OK 1 #define ERROR 0 typedef struct myElemType{ bool isOperator; int data; }ElemType; typedef int Status; using namespace std; class Stack{ public: //Status InitStack(void) //数据结构的写法 void InitStack(void){ bottom=(ElemType*)malloc(STACK_SIZE*sizeof(ElemType)); //if(!bottom) //return ERROR; top=bottom; stacksize=STACK_SIZE; //return OK; } //Status push(ElemType e) //数据结构的写法 void push(ElemType e){ *top=e; top++; if(top-bottom==stacksize){ bottom=(ElemType*)realloc(bottom,(STACKINCREMENT+stacksize)*sizeof(ElemType)); //if(!bottom) //return ERROR; top=bottom+stacksize; stacksize+=STACKINCREMENT; } //return OK; } Status pop(ElemType *e){ if(top==bottom) return ERROR; top--; *e=*top; return OK; } ElemType* Top(void){ if(top==bottom) return NULL; return top-1; } void printTtoB(void){ if(top==bottom) return; for(ElemType* i=top-1;i!=bottom;i--){ if(i->isOperator==0) cout << i->data << " "; else cout << (char)i->data << " "; } if(bottom->isOperator==0) cout << bottom->data; else cout << (char)(bottom->data); } void printBtoT(void){ if(top==bottom) return; for(ElemType* i=bottom;i!=top-1;i++){ if(i->isOperator==0) cout << i->data << " "; else cout << (char)i->data << " "; } if((top-1)->isOperator==0) cout << (top-1)->data; else cout << (char)((top-1)->data); } void DestroyStack(void){ do{ free(top); top--; }while(top!=bottom); top=NULL; bottom=NULL; stacksize=0; } private: ElemType *bottom; ElemType *top; int stacksize; }; Stack Op,R; int main(){ map <char,int> priority; //定义符号的优先级 priority['^']=3; priority['*']=2; priority['/']=2; priority['+']=1; priority['-']=1; ElemType temp; char c[105],o; int num=0; bool isnum=1,isminus=0; Op.InitStack(); R.InitStack(); cin >> c; for(int i=0;i<strlen(c);i++){ if(c[i]>='0' && c[i]<='9'){ //2.1)如果遇到数,直接入栈R,此时注意负数的判断; isnum=0; if(!isminus) //负数的判断 num=num*10+c[i]-'0'; else num=num*10-c[i]+'0'; if(i==strlen(c)-1 || c[i+1]<'0' || c[i+1]>='9'){ temp.data=num; temp.isOperator=0; R.push(temp); num=0; isminus=0; } } else if(c[i]=='('){ //如果遇到左括号'(',则直接压入运算符栈Op; temp.data=(int)c[i]; temp.isOperator=1; Op.push(temp); isnum=1; isminus=0; } else if(c[i]==')') //如果遇到右括号')',则依次弹出运算符栈Op栈顶的运算符,并压入栈R,直到遇到左括号为止,并丢弃这对括号; while(Op.pop(&temp) && temp.data!='(') R.push(temp); else{ //2.2)如果遇到运算符,比较其与运算符栈Op栈顶运算符的优先级 if(isnum && c[i]=='-') //负数的判断 isminus=1; else{ while(Op.Top()!=NULL && Op.Top()->data!='(' && priority[((char)(Op.Top()->data))]>=priority[c[i]]){ //如果运算符栈Op为空,或运算符栈Op栈顶运算符为左括号'(',或该运算符的优先级比栈顶运算符的高,则直接将此运算符入栈; Op.pop(&temp); R.push(temp); } temp.data=(int)c[i]; temp.isOperator=1; Op.push(temp); } isnum=1; } } while(R.pop(&temp)) Op.push(temp); Op.printTtoB(); cout << endl; while(Op.Top()!=NULL){ //(2)从Op弹出元素,直到Op为空; Op.pop(&temp); if(temp.isOperator){ //2.2)如果遇到运算符,用运算符对栈顶元素和次顶元素做相应的计算(次顶元素 运算符 栈顶元素),然后输出: o=(char)(temp.data); //运算符 R.pop(&temp); num=temp.data; //栈顶元素 R.pop(&temp); //次顶元素 if(o=='+') temp.data+=num; else if (o=='-') temp.data-=num; else if (o=='*') temp.data*=num; else if (o=='/') temp.data/=num; else if (o=='^') temp.data=(int)pow(temp.data,num); R.push(temp); R.printBtoT(); cout << " "; Op.printTtoB(); cout << endl; } else //2.1)如果遇到数,直接入栈R; R.push(temp); } return 0; }