【数据结构】表达式的转换

こ雲淡風輕ζ 提交于 2019-11-27 18:09:39

【题目链接】

https://www.luogu.org/problem/P1175

题目描述

平常我们书写的表达式称为中缀表达式,因为它将运算符放在两个操作数中间,许多情况下为了确定运算顺序,括号是不可少的,而中缀表达式就不必用括号了。

后缀标记法:书写表达式时采用运算紧跟在两个操作数之后,从而实现了无括号处理和优先级处理,使计算机的处理规则简化为:从左到右顺序完成计算,并用结果取而代之。

例如: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行少一个运算符和一个操作数,最后一行只有一个数字,表示运算结果。

输入输出样例

输入 #1
8-(3+2*6)/5+4
输出 #1
8 3 2 6 * + 5 / - 4 + 
8 3 12 + 5 / - 4 + 
8 15 5 / - 4 + 
8 3 - 4 + 
5 4 + 
9

说明/提示

运算的结果可能为负数,/以整除运算。并且中间每一步都不会超过2^{31}。字符串长度不超过100


 

【参考博客】:

https://www.luogu.org/problemnew/solution/P1175

 0AND1STORY这位大佬的题解异常清晰简洁,代码风格一流。

 

【实现过程】

1、从中缀表达式转变成后缀表达式。

转换后缀表达式主要使用栈来实现,只需要枚举每一位,分情况处理即可。

一共有四种情况,如下:

  • 数字:直接输出即可。
  • 左括号:直接加入栈中。
  • 右括号:一直弹出栈中元素并输出,直到遇到左括号为止。最后弹出左括号但不输出左括号。
  • 一般运算符:一直弹出栈中元素并输出,直到遇到比自己运算优先级低的运算符。最后把当前运算符压入栈中。

最后若计算完成但栈中还有元素,弹出栈中所有元素并输出。

输出的字符串即为转换完成的后缀表达式。


 

 1 /* 返回每个运算符对应的优先级 */
 2 inline int priority(const char& ch) {
 3     switch ( ch ){
 4         case '+' :
 5         case '-' : return 1 ;
 6 
 7         case '*' :
 8         case '/' : return 2 ;
 9 
10         case '^' : return 3 ;
11 
12         case '(' :
13         case ')' : return 0 ;
14     }
15 }
16 
17 /* 将中缀表达式转换为后缀表达式 */
18 inline string toSuffix(const string& s) {
19 
20     string res = "";
21     stack <char> Op;
22 
23     for(int i=0;i<s.length();i++){
24         if( isdigit(s[i]) ){
25             res += s[i] ;
26         }else if( s[i] == '(' ){
27             Op.push(s[i]);
28         }else if( s[i] == ')' ){
29             //在过程中算一遍
30             while( !Op.empty() && (char)Op.top() != '(' )
31                 res += Op.top() , Op.pop();
32             //最后弹出'('
33             Op.pop();
34         }else{
35             //保持优先级 保持 栈顶 当遇到 优先级低于栈顶时输出
36             while( !Op.empty() &&  priority( Op.top() ) >= priority( s[i] ) )
37                 res += Op.top() , Op.pop();
38             
39             //此时只有两种情况,操作符压栈,1、栈空 2、在计算该运算符时都运算了一遍了.
40             Op.push(s[i]);
41         }
42     }
43     while( !Op.empty() ){
44         res += Op.top() ;
45         Op.pop() ;
46     }
47     return res ;
48 }

 

2. 计算后缀表达式的值,并输出计算过程:

计算后缀表达式也使用栈来实现,计算过程同理要分情况处理。

一共分为两种情况,如下:

  • 数字:直接压入栈即可。
  • 运算符:取出栈顶的两个数进行相应的运算,将计算结果压入栈中。一定要注意取出元素的计算顺序!
  • 先取出的数为X数,后取出的数为被X数!每次完成当前计算后输出计算过程。

输出计算过程的方法为:

  1. 先输出已计算完成的数,即所有的栈中元素(需要按照从栈底到栈顶的顺序)。
  2. 再输出还未计算的数,即字符串中当前字符到末尾的字符串。
 1 /* 根据运算符计算相应的运算结果 */
 2 inline int calcNum(const int& a, const int& b, const int& symbol) {
 3     switch( symbol ){
 4         case '+' : return a + b ;
 5         case '-' : return a - b ;
 6         case '*' : return a * b ;
 7         case '/' : return a / b ;
 8         case '^' : return (int)pow( a,b );
 9     }
10 }
11 
12 /* 将后缀表达式加上空格后输出 */
13 inline void printSuffix(const string& s) {
14     for(int i=0;i<s.length();i++)
15         cout << s[i] << (i==s.length()-1?'\n':' ');
16 }
17 
18 /* 计算后缀表达式并输出计算过程 */
19 inline void calc(const string& s) {
20 
21     list < int > Num;
22 
23     printSuffix(s);
24 
25     for(int i=0;i<s.length();i++){
26         if( isdigit(s[i]) ){ //数
27             Num.push_back( s[i]-'0' );
28         }else{
29             int u , v ;
30             v = Num.back() ;  Num.pop_back() ;
31             u = Num.back() ;  Num.pop_back() ;
32             Num.push_back( calcNum( u , v , s[i] ) ) ;
33             //过程中打印
34             for( auto x : Num )
35                 cout << x << " ";
36             for( int j = i+1 ; j<s.length() ; j++ )
37                 cout << s[j] << ( j == s.length() - 1 ? '\n':' ') ;
38         }
39 
40     }
41     //printf("%d\n",Num.begin() );
42 }

 

最后附上完整代码:

 

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 
  4 
  5 /* 返回每个运算符对应的优先级 */
  6 inline int priority(const char& ch) {
  7     switch ( ch ){
  8         case '+' :
  9         case '-' : return 1 ;
 10 
 11         case '*' :
 12         case '/' : return 2 ;
 13 
 14         case '^' : return 3 ;
 15 
 16         case '(' :
 17         case ')' : return 0 ;
 18     }
 19 }
 20 
 21 /* 将中缀表达式转换为后缀表达式 */
 22 inline string toSuffix(const string& s) {
 23 
 24     string res = "";
 25     stack <char> Op;
 26 
 27     for(int i=0;i<s.length();i++){
 28         if( isdigit(s[i]) ){
 29             res += s[i] ;
 30         }else if( s[i] == '(' ){
 31             Op.push(s[i]);
 32         }else if( s[i] == ')' ){
 33             //在过程中算一遍
 34             while( !Op.empty() && (char)Op.top() != '(' )
 35                 res += Op.top() , Op.pop();
 36             //最后弹出'('
 37             Op.pop();
 38         }else{
 39             //保持优先级 保持 栈顶 当遇到 优先级低于栈顶时输出
 40             while( !Op.empty() &&  priority( Op.top() ) >= priority( s[i] ) )
 41                 res += Op.top() , Op.pop();
 42             
 43             //此时只有两种情况,操作符压栈,1、栈空 2、在计算该运算符时都运算了一遍了.
 44             Op.push(s[i]);
 45         }
 46     }
 47     while( !Op.empty() ){
 48         res += Op.top() ;
 49         Op.pop() ;
 50     }
 51     return res ;
 52 }
 53 
 54 /* 根据运算符计算相应的运算结果 */
 55 inline int calcNum(const int& a, const int& b, const int& symbol) {
 56     switch( symbol ){
 57         case '+' : return a + b ;
 58         case '-' : return a - b ;
 59         case '*' : return a * b ;
 60         case '/' : return a / b ;
 61         case '^' : return (int)pow( a,b );
 62     }
 63 }
 64 
 65 /* 将后缀表达式加上空格后输出 */
 66 inline void printSuffix(const string& s) {
 67     for(int i=0;i<s.length();i++)
 68         cout << s[i] << (i==s.length()-1?'\n':' ');
 69 }
 70 
 71 /* 计算后缀表达式并输出计算过程 */
 72 inline void calc(const string& s) {
 73 
 74     list < int > Num;
 75 
 76     printSuffix(s);
 77 
 78     for(int i=0;i<s.length();i++){
 79         if( isdigit(s[i]) ){ //数
 80             Num.push_back( s[i]-'0' );
 81         }else{
 82             int u , v ;
 83             v = Num.back() ;  Num.pop_back() ;
 84             u = Num.back() ;  Num.pop_back() ;
 85             Num.push_back( calcNum( u , v , s[i] ) ) ;
 86             //过程中打印
 87             for( auto x : Num )
 88                 cout << x << " ";
 89             for( int j = i+1 ; j<s.length() ; j++ )
 90                 cout << s[j] << ( j == s.length() - 1 ? '\n':' ') ;
 91         }
 92 
 93     }
 94     //printf("%d\n",Num.begin() );
 95 }
 96 
 97 int main()
 98 {
 99     string s ;
100     cin >> s ;
101     s = toSuffix(s);
102     calc(s);
103     return 0;
104 }
表达式转换

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!