逆波兰表达式——中缀表达式转后缀表达式
Posted lanhaicode
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了逆波兰表达式——中缀表达式转后缀表达式相关的知识,希望对你有一定的参考价值。
逆波兰表达式
先说一下中缀表达式,平时我们使用的运算表达式就是中缀表达式,例如1+3*2,中缀表达式的特点就是:二元运算符总是置于与之相关的两个运算对象之间
人读起来比较好理解,但是计算机处理起来就很麻烦,运算顺序往往因表达式的内容而定,不具规律性
后缀表达式,后缀表达式的特点就是:每一运算符都置于其运算对象之后,以上面的中缀表达式1+2*3为例子,转为后缀表达式就是123*+
下面先分析怎么把中缀表达式转换为后缀表达式,这里我们考虑六种操作符‘+‘、‘-‘、‘*‘、‘/‘、‘(‘、‘)‘,完成中缀转后缀我们需要两个数组,都以栈的方式来操作,一个数组用来存放后缀表达式(char num[100]),
一个数组用来临时存放操作数(char opera[100])(这里说临时存放,是因为最后都要入栈到后缀表达式数组num中,这个数组就相当于一个中转站)
1、从左往右扫描中缀表达式(这里我们以1*(2+3)为例)
2、如果是数字那么将其直接入栈到数组num中
3、如果是操作数,需要进一步判断
(1)如果是左括号‘(‘直接入栈到数组opera中
(2)如果是运算符(‘+‘、‘-‘、‘*‘、‘/‘),先判断数组opera的栈顶的操作数的优先级(如果是空栈那么直接入栈到数组opera),如果是左括号那么直接入栈到数组opera中,如果栈顶是运算符,且栈顶运算符的优先级大于该运算符
那么将栈顶的运算符出栈,并入栈到数组num中,重复步骤3,如果栈顶运算符优先级小于该运算符,那么直接将该运算符入栈到opera中
(3)如果是右括号‘)‘,那么说明在opera数组中一定有一个左括号与之对应(在你没输错的情况下),那么将opera中的运算符依次出栈,并入栈到num中,直到遇到左括号‘(‘(注意左括号不用入栈到num)
4、如果中缀表达式扫描完了,那么将opera中的操作数依次出栈,并入栈到num中就可以了,如果没有没有扫描完重复1-3步
上面就是中缀表达式转后缀表达式的步骤了,下面用图来直观的了解一下这个过程
需要注意的是:opera中操作数,越靠近栈顶,优先级越高,下面附上实现代码
1 void PexpretoSexpre(char *ss) 2 { 3 char num[100] = "0"; /* 存储后缀表达式 */ 4 char opera[100] = "0"; /* 存储运算符 */ 5 /* 6 num----j 7 opera----op 8 ss----i 9 */ 10 int i, j, op; 11 12 op = i = j = 0; 13 14 while (ss[i] != ‘\\0‘) 15 { 16 if (isdigit(ss[i])) /* 如果是数字 */ 17 { 18 num[j] = ss[i]; /* 数字直接入后缀表达式栈 */ 19 j++; 20 i++; 21 } 22 else 23 { 24 switch (ss[i]) /* 如果是操作数 */ 25 { 26 case ‘+‘: 27 { 28 if (op == 0) /* 如果是空栈 */ 29 { 30 PushOperation(opera, ss, &op, &i); /* 入运算符栈 */ 31 break; 32 } 33 if (opera[op-1] == ‘+‘ || opera[op-1] == ‘-‘ || opera[op-1] == ‘*‘ || opera[op-1] == ‘/‘ || opera[op-1] == ‘)‘ || opera[op-1] == ‘(‘) 34 { 35 switch (opera[op-1]) 36 { 37 case ‘+‘: 38 { 39 PushOperation(opera, ss, &op, &i); 40 break; 41 } 42 case ‘-‘: 43 { 44 PushOperation(opera, ss, &op, &i); 45 break; 46 } 47 case ‘*‘: 48 { /* 加法优先级低于乘法 */ 49 num[j] = opera[op-1]; /* 将操作数出栈 */ 50 opera[op-1] = ss[i]; /* 将新的操作数压入栈中 */ 51 j++; 52 i++; 53 break; 54 } 55 case ‘/‘: 56 { 57 num[j] = opera[op-1]; 58 opera[op-1] = ss[i]; 59 j++; 60 i++; 61 break; 62 } 63 case ‘(‘: 64 { 65 PushOperation(opera, ss, &op, &i); 66 break; 67 } 68 } 69 } 70 break; 71 } 72 case ‘-‘: 73 { 74 if (op == 0) 75 { 76 PushOperation(opera, ss, &op, &i); 77 break; 78 } 79 if (opera[op-1] == ‘+‘ || opera[op-1] == ‘-‘ || opera[op-1] == ‘*‘ || opera[op-1] == ‘/‘ || opera[op-1] == ‘)‘ || opera[op-1] == ‘(‘) 80 { 81 switch (opera[op-1]) 82 { 83 case ‘+‘: 84 { 85 PushOperation(opera, ss, &op, &i); 86 break; 87 } 88 case ‘-‘: 89 { 90 PushOperation(opera, ss, &op, &i); 91 break; 92 } 93 case ‘*‘: 94 { 95 num[j] = opera[op-1]; 96 opera[op-1] = ss[i]; 97 j++; 98 i++; 99 break; 100 } 101 case ‘/‘: 102 { 103 num[j] = opera[op-1]; 104 opera[op-1] = ss[i]; 105 j++; 106 i++; 107 break; 108 } 109 case ‘(‘: 110 { 111 PushOperation(opera, ss, &op, &i); 112 break; 113 } 114 } 115 } 116 break; 117 } 118 case ‘*‘: 119 { 120 if (op == 0) 121 { 122 PushOperation(opera, ss, &op, &i); 123 break; 124 } 125 if (opera[op-1] == ‘+‘ || opera[op-1] == ‘-‘ || opera[op-1] == ‘*‘ || opera[op-1] == ‘/‘ || opera[op-1] == ‘)‘ || opera[op-1] == ‘(‘) 126 { 127 switch (opera[op-1]) 128 { 129 case ‘+‘: 130 { 131 PushOperation(opera, ss, &op, &i); 132 break; 133 } 134 case ‘-‘: 135 { 136 PushOperation(opera, ss, &op, &i); 137 break; 138 } 139 case ‘*‘: 140 { 141 PushOperation(opera, ss, &op, &i); 142 break; 143 } 144 case ‘/‘: 145 { 146 PushOperation(opera, ss, &op, &i); 147 break; 148 } 149 case ‘(‘: 150 { 151 PushOperation(opera, ss, &op, &i); 152 break; 153 } 154 } 155 } 156 break; 157 } 158 case ‘/‘: 159 { 160 if (op == 0) 161 { 162 PushOperation(opera, ss, &op, &i); 163 break; 164 } 165 if (opera[op-1] == ‘+‘ || opera[op-1] == ‘-‘ || opera[op-1] == ‘*‘ || opera[op-1] == ‘/‘ || opera[op-1] == ‘)‘ || opera[op-1] == ‘(‘) 166 { 167 switch (opera[op-1]) 168 { 169 case ‘+‘: 170 { 171 PushOperation(opera, ss, &op, &i); 172 break; 173 } 174 case ‘-‘: 175 { 176 PushOperation(opera, ss, &op, &i); 177 break; 178 } 179 case ‘*‘: 180 { 181 PushOperation(opera, ss, &op, &i); 182 break; 183 } 184 case ‘/‘: 185 { 186 PushOperation(opera, ss, &op, &i); 187 break; 188 } 189 case ‘(‘: 190 { 191 PushOperation(opera, ss, &op, &i); 192 break; 193 } 194 } 195 } 196 break; 197 } 198 case ‘(‘: 199 { 200 PushOperation(opera, ss, &op, &i); 201 break; 202 } 203 case ‘)‘: /* 如果遇到右括号 */ 204 { 205 while (opera[op-1] != ‘(‘) 206 { 207 num[j] = opera[op-1]; /* 将运算符栈中的元素依次入栈到后缀表达式栈中,直到遇到左括号为止 */ 208 j++; 209 op--; 210 } 211 op--; 212 i++; 213 break; 214 } 215 default: 216 { 217 printf("传入表达式不符合要求\\n"); 218 exit(0); 219 } 220 221 } 222 } 223 } 224 while (op != 0) 225 { 226 num[j] = opera[op-1]; /* 将运算符栈中的元素依次入栈到后缀表达式栈中 */ 227 j++; 228 op--; 229 } 230 num[j] = ‘\\0‘; 231 i = 0; 232 while (num[i] != ‘\\0‘) /* 将后缀表达式存储到传入的形参ss中 */ 233 { 234 ss[i] = num[i]; 235 i++; 236 } 237 ss[i] = ‘\\0‘; 238 } 239 240 /* Function: 入运算符栈*/ 241 void PushOperation(char *opera, char *ss, int *op, int *s) 242 { 243 opera[*op] = ss[*s]; 244 (*op)++; 245 (*s)++; 246 }
后缀表达式的计算
完成了中缀表达式转后缀表达式,接下来就是后缀表达式的计算了,后缀表达式的计算比中缀转后缀要稍微简单一点,只需要对我们转换好的后缀表达式从左往右依次扫描,并依次入栈就行了,
意思是只需要用一个数组(double num[100])就OK了
需要考虑的情况如下
1、如果是数字,那么直接入栈到num中
2、如果是运算符,将栈顶的两个数字出栈(因为我们考虑的运算符加、减、乘、除都是双目运算符,只需要两个操作数),出栈后对两个数字进行相应的运算,并将运算结果入栈
3、直到遇到‘\\0‘
下面用几张图,来直观了解下这个过程,以上面转换好的后缀表达式"123+*"为例(这里用ss来存储后缀表达式,num来存储计算结果,注意不要与上面图中num搞混淆了)
(注意:这里将计算结果5入栈后,栈顶从之前的[3]变成[2])
到这里后缀表达式的计算就结束了,下面附上实现代码
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define MAX 100 5 6 void JudgeFopen_s(errno_t err); 7 void ReadFile(FILE *fp, char *ss); 8 double TransformCtoD(char ch); 9 10 int main() 11 { 12 FILE *fp; 13 errno_t err; 14 15 char ss[MAX]; /* 存储逆波兰表达式 */ 16 int i = 0; 17 int j = 0; 18 double num[MAX]; /* 栈 */ 19 20 err = fopen_s(&fp, "E:\\\\ww.txt", "r"); 21 22 JudgeFopen_s(err); 23 ReadFile(fp, ss); 24 25 while (ss[i] != ‘\\0‘) 26 { 27 if (ss[i] >= ‘0‘ && ss[i] <= ‘9‘) /* 如果是数字 */ 28 { 29 /* 因为num是char类型的,需要转换为double类型方便计算 */ 30 num[j] = TransformCtoD(ss[i]); /* 将数字存储到栈中 */ 31 j++; 32 i++; 33 } 34 else if (ss[i] == ‘+‘ || ss[i] == ‘-‘ || ss[i] == ‘*‘ || ss[i] == ‘/‘) 35 { 36 switch (ss[i]) 37 { 38 case ‘+‘: 39 { 40 num[j-2] = num[j-1] + num[j-2]; 41 j = j-1; 42 i++; /* 表达式中的下一个元素 */ 43 break; 44 } 45 case ‘-‘: 46 { 47 num[j-2] = num[j-2] - num[j-1]; 48 j = j-1; 49 i++; 50 break; 51 } 52 case ‘*‘: 53 { 54 num[j-2] = num[j-2] * num[j-1]; 55 j = j-1; 56 i++; 57 break; 58 } 59 case ‘/‘: 60 { 61 num[j-2] = num[j-2] / num[j-1]; 62 j = j-1; 63 i++; 64 break; 65 } 66 default:exit(0); 67 } 68 } 69 else if (ss[i] == ‘\\n‘) 70 { 71 break; 72 } 73 } 74 75 printf("%lf", num[0]); 76 return 0; 77 } 78 79 void JudgeFopen_s(errno_t err) 80 { 81 if (err != 0) 82 { 83 printf("文件打开失败\\n"); 84 system("pause"); 85 exit(0); 86 } 87 } 88 void ReadFile(FILE *fp, char *ss) 89 { 90 int i = 0; 91 92 while (!feof(fp)) 93 { 94 fscanf_s(fp, "%c", &ss[i]); 95 i++; 96 } 97 ss[i-1] = ‘\\0‘; 98 } 99 double TransformCtoD(char ch) 100 { 101 return (double)(ch - ‘0‘); 102 }
以上是关于逆波兰表达式——中缀表达式转后缀表达式的主要内容,如果未能解决你的问题,请参考以下文章
算法逆波兰式(后缀表达式)的计算 & 中缀表达式转后缀表达式(逆波兰式)