【题目链接】
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 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 }
