当前位置:   article > 正文

《数据结构》:中缀表达式转后缀表达式 + 后缀表达式的计算

中缀表达式转后缀表达式

  • 补充了一个判断输入中缀表达式合法性的代码:

《数据结构》:中缀表达式合法性判断_Amentos的博客-CSDN博客

 

目录

一、基本概念

二、中缀表达式转后缀表达式

   例       中缀表达式  2*(3+5)+7/1-4  转换为后缀表达式

三、后缀表达式的计算

   例       后缀表达式  2 3 5 + * 7 1 / + 4 -  的计算

四、算法实现

五、算法改进


一、基本概念

1、中缀表达式:

        操作符以中缀形式位于运算数中间(如:3+2),是我们日常通用的算术和逻辑公式表示方法。

2、后缀表达式:

        又称逆波兰式Reverse Polish Notation - RPN),操作符以后缀形式位于两个运算数后(如:3+2的后缀表达形式就是3 2 +)。

3、前缀表达式:

        又称波兰式Polish Notation),操作符以前缀形式位于两个运算数前(如:3+2的前缀表达形式就是+ 3 2)。

中缀表达式往往需要使用括号将操作符和对应的操作数括起来,用于指示运算的次序

e.g:5*(2+1) 虽然 * 的优先级高于 +  ,但括号的存在表示应优先执行括号内的 + 运算。

中缀表达式适合于人类的思维结构和运算习惯,但并不适用于计算机

尤其是包含括号的中缀表达式,对计算机而言是非常复杂的结构。

适用于计算机的后缀表达式

与中缀表达式不同,后缀表达式不需要使用括号来标识操作符的优先级。

后缀表达式的计算按 操作符 从左到右出现的顺序依次执行(不考虑运算符之间的优先级),对于计算机而言是比较简单的结构。

二、中缀表达式转后缀表达式

从左至右依次遍历中缀表达式各个字符(需要准备一个字符栈存储操作符和括号)

1、字符为 运算数 

直接送入后缀表达式(注:需要先分析出完整的运算数)。

2、字符为 左括号

直接入栈(注:左括号入栈后优先级降至最低)。

3、字符为 右括号

直接出栈,并将出栈字符依次送入后缀表达式,直到栈顶字符为左括号(左括号也要出栈,但不送入后缀表达式)。

总结:只要满足 栈顶为左括号 即可进行最后一次出栈。

4、字符为 操作符

若栈空,直接入栈。

若栈非空,判断栈顶操作符,若栈顶操作符优先级低于该操作符,该操作符入栈;否则一直出栈,并将出栈字符依次送入后缀表达式,直到栈空或栈顶操作符优先级低于该操作符,该操作符再入栈。

总结:只要满足 栈空 或者 优先级高于栈顶操作符 即可停止出栈,并将该操作符入栈。

5、重复以上步骤直至遍历完成中缀表达式,接着判断字符栈是否为空,非空则直接出栈,并将出栈字符依次送入后缀表达式。

注:中缀表达式遍历完成,栈中可能还有字符未输出,故需要判断栈空。

          中缀表达式  2*(3+5)+7/1-4  转换为后缀表达式

从左至右依次遍历中缀表达式各个字符:

第一个字符为运算数,直接输出:


第二个字符为操作符,满足 栈空/优先级高于栈顶操作符 条件,该操作符入栈:


第三个字符为左括号,直接入栈(入栈后优先级降至最低):


第四个字符为运算数,直接输出:


第五个字符为操作符,满足 栈空/优先级高于栈顶操作符 条件,该操作符入栈:


第六个字符为运算数,直接输出:


第七个字符为右括号,直接出栈并输出,直到栈顶为左括号时进行最后一次出栈(不输出):



第八个字符为操作符,不满足 栈空/优先级高于栈顶操作符 条件,出栈直至满足条件



第九个字符为运算数,直接输出:


第十个字符为操作符,满足 栈空/优先级高于栈顶操作符 条件,该操作符入栈:


第十一个字符为运算数,直接输出:


第十二个字符为操作符,不满足 栈空/优先级高于栈顶操作符 条件,出栈直至满足条件:




第十三个字符为运算数,直接输出:


中缀表达式遍历完成判断字符栈中是否还有操作符,如有则出栈并输出:


转换完成:

三、后缀表达式的计算

从左至右依次遍历后缀表达式各个字符(需要准备一个运算数栈存储运算数和操作结果)

1、字符为 运算数

直接入栈(注:需要先分析出完整的运算数并将其转换为对应的数据类型)

2、字符为 操作符

连续出栈两次,使用出栈的两个数据进行相应计算,并将计算结果入栈

e.g:第一个出栈的运算数为 a ,第二个出栈的运算数为 b ,此时的操作符为 - ,则计算 b-a 注:a和b顺序不能反),并将结果入栈。

3、重复以上步骤直至遍历完成后缀表达式,最后栈中的数据就是中缀表达式的计算结果。

          后缀表达式  2 3 5 + * 7 1 / + 4 -  的计算

从左至右依次遍历后缀表达式各个字符:

第一个字符为运算数,直接入栈:


第二个字符为运算数,直接入栈:


第三个字符为运算数,直接入栈:


第四个字符为操作符,直接出栈两次:


继续出栈:


执行: 第二次出栈运算数    操作符    第一次出栈运算数 

即:3 + 5

结果:8

将计算结果入栈:


第五个字符为操作符,直接出栈两次:


执行: 第二次出栈运算数    操作符    第一次出栈运算数 

即:2 * 8

结果:16

将计算结果入栈:


第六个字符为运算数,直接入栈:


第七个字符为运算数,直接入栈:


第八个字符为操作符,直接出栈两次:


执行: 第二次出栈运算数    操作符    第一次出栈运算数 

即:7 / 1

结果:7

将计算结果入栈:


第九个字符为操作符,直接出栈两次:


执行: 第二次出栈运算数    操作符    第一次出栈运算数 

即:16 + 7

结果:23

 将计算结果入栈:


第十个字符为运算数,直接入栈:


第十一个字符为操作符,直接出栈两次:


执行: 第二次出栈运算数    操作符    第一次出栈运算数 

即:23 - 4

结果:19

将计算结果入栈:


后缀表达式遍历完成,栈中数据即为最终计算结果:

四、算法实现

程序代码:

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<malloc.h>
  4. #include<string.h>
  5. #include<ctype.h>
  6. #define ERROR 0
  7. #define OK 1
  8. #define STACK_INT_SIZE 10 /*存储空间初始分配量*/
  9. #define STACKINCREMENT 5 /*存储空间分配增量*/
  10. #define M 50
  11. typedef char ElemType; /*定义字符数据类型*/
  12. typedef double ElemType2; /*定义运算数数据类型*/
  13. /*字符栈*/
  14. typedef struct{
  15. ElemType *base;
  16. ElemType *top;
  17. int stacksize;
  18. }SqStack;
  19. /*运算数栈*/
  20. typedef struct{
  21. ElemType2 *base;
  22. ElemType2 *top;
  23. int stacksize;
  24. }NStack;
  25. int InitStack(SqStack *S); /*构造空栈*/
  26. int push(SqStack *S,ElemType e); /*入栈*/
  27. int Pop(SqStack *S,ElemType *e); /*出栈*/
  28. int StackEmpty(SqStack *s); /*栈空判断*/
  29. void in2post(ElemType *str,ElemType *p); /*中缀表达式转后缀表达式*/
  30. double cal_post(char *str); /*计算后缀表达式*/
  31. /*字符栈初始化*/
  32. int InitStack(SqStack *S){
  33. S->base=(ElemType *)malloc(STACK_INT_SIZE * sizeof(ElemType));
  34. if(!S->base)
  35. return ERROR; //分配失败
  36. S->top = S->base;
  37. S->stacksize = STACK_INT_SIZE;
  38. return OK;
  39. }/*InitStack*/
  40. /*运算数栈初始化*/
  41. int InitStack_N(NStack *S){
  42. S->base=(ElemType2 *)malloc(STACK_INT_SIZE * sizeof(ElemType2));
  43. if(!S->base)
  44. return ERROR;
  45. S->top = S->base;
  46. S->stacksize = STACK_INT_SIZE;
  47. return OK;
  48. }
  49. /*字符栈入栈*/
  50. int Push(SqStack *S,ElemType e){
  51. //判断栈满
  52. if(S->top - S->base >= S->stacksize){
  53. S->base = (ElemType *)realloc(S->base,(S->stacksize + STACKINCREMENT)*sizeof(ElemType));
  54. if(NULL == S->base) //分配失败
  55. return ERROR;
  56. S->top = S->base + S->stacksize;
  57. S->stacksize = S->stacksize+STACKINCREMENT;
  58. }
  59. *S->top = e;
  60. S->top++;
  61. return OK;
  62. }
  63. /*运算数栈入栈*/
  64. int Push_N(NStack *S,ElemType2 e){
  65. if(S->top - S->base >= S->stacksize){
  66. S->base = (ElemType2 *)realloc(S->base,(S->stacksize + STACKINCREMENT)*sizeof(ElemType2));
  67. if(NULL == S->base)
  68. return ERROR;
  69. S->top = S->base + S->stacksize;
  70. S->stacksize = S->stacksize+STACKINCREMENT;
  71. }
  72. *S->top = e;
  73. S->top++;
  74. return OK;
  75. }
  76. /*字符栈出栈*/
  77. int Pop(SqStack *S,ElemType *e){
  78. //判断栈空
  79. if(S->top == S->base)
  80. return ERROR;
  81. S->top--;
  82. *e=*S->top;
  83. return OK;
  84. }/*Pop*/
  85. /*运算数栈出栈*/
  86. int Pop_N(NStack *S,ElemType2 *e){
  87. if(S->top == S->base)
  88. return ERROR;
  89. S->top--;
  90. *e=*S->top;
  91. return OK;
  92. }
  93. /*判断栈空*/
  94. int StackEmpty(SqStack *s){
  95. if(s->top == s->base)
  96. return OK;
  97. return ERROR;
  98. }/*StackEmpty*/
  99. //str为待转换的中缀表达式字符串,p为转换后的后缀表达式字符串
  100. void in2post(ElemType *str,ElemType *p){ /*infix to postfix*/
  101. SqStack s;
  102. InitStack(&s); //初始化一个空字符栈
  103. ElemType e;
  104. int i;
  105. int j=0;
  106. for(i=0 ; i<strlen(str) ; i++) //遍历中缀表达式
  107. {
  108. //遇到数字和小数点直接输出
  109. //使用循环完整接收一个运算数
  110. while(isdigit(str[i]) || '.'==str[i])
  111. {
  112. p[j++]=str[i++];
  113. if(!isdigit(str[i]) && '.'!=str[i])
  114. p[j++]=' '; //一个数字完整输出后使用空格与其它运算符或数字分隔开
  115. }
  116. //遇到左括号直接入栈
  117. if('('==str[i])
  118. Push(&s,str[i]);
  119. //遇到右括号直接出栈,直到栈顶为左括号
  120. if(')'==str[i])
  121. {
  122. while('(' != *(s.top-1))
  123. {
  124. Pop(&s,&e);
  125. p[j++]=e;
  126. p[j++]=' ';
  127. }
  128. Pop(&s,&e); //左括号出栈但不输出
  129. }
  130. //遇到+或—
  131. //1.栈空/栈顶为左括号:直接入栈
  132. //2.否则一直出栈,直到栈空/栈顶为左括号,再入栈
  133. if('+'==str[i] || '-'==str[i])
  134. {
  135. while(!StackEmpty(&s) && '('!=*(s.top-1))
  136. {
  137. Pop(&s,&e);
  138. p[j++]=e;
  139. p[j++]=' ';
  140. }
  141. Push(&s,str[i]);
  142. }
  143. //遇到*或/
  144. //1.栈空/栈顶为左括号/栈顶操作符为+ or -:直接入栈
  145. //2.否则一直出栈,直到满足1,再入栈
  146. if('*'==str[i] || '/'==str[i] || '%'==str[i])
  147. {
  148. while(!StackEmpty(&s) && '('!=*(s.top-1) && '+'!=*(s.top-1) && '-'!=*(s.top-1))
  149. {
  150. Pop(&s,&e);
  151. p[j++]=e;
  152. p[j++]=' ';
  153. }
  154. Push(&s,str[i]);
  155. }
  156. }
  157. //中缀表达式遍历完成,还需检查栈中是否有未输出字符
  158. //判断栈空,非空则直接出栈并输出(左括号不用输出)
  159. while(!StackEmpty(&s)){
  160. Pop(&s,&e);
  161. if('('!=e)
  162. {
  163. p[j++]=e;
  164. p[j++]=' ';
  165. }
  166. }
  167. p[--j]='\0';
  168. }/*infix2postfix*/
  169. //str为待计算的后缀表达式,返回值为计算结果
  170. double cal_post(char *str){ /*计算后缀表达式*/
  171. int i;
  172. ElemType2 e,a,b;
  173. char d[M];
  174. NStack n;
  175. InitStack_N(&n); //初始化一个运算数栈保存运算数
  176. for(i=0;i<strlen(str);i++)
  177. {
  178. int j=0;
  179. while(isdigit(str[i]) || '.'==str[i])
  180. {
  181. d[j++]=str[i++];
  182. d[j]='\0';
  183. if(!isdigit(str[i]) && '.'!=str[i])
  184. {
  185. e=atof(d); //使用atof()将字符串形式的运算数转换为double型数据
  186. Push_N(&n,e); //运算数入栈
  187. }
  188. }
  189. switch(str[i])
  190. {
  191. case '+':
  192. Pop_N(&n,&b);
  193. Pop_N(&n,&a);
  194. Push_N(&n,a+b);
  195. break;
  196. case '-':
  197. Pop_N(&n,&b);
  198. Pop_N(&n,&a);
  199. Push_N(&n,a-b);
  200. break;
  201. case '*':
  202. Pop_N(&n,&b);
  203. Pop_N(&n,&a);
  204. Push_N(&n,a*b);
  205. break;
  206. case '/':
  207. Pop_N(&n,&b);
  208. Pop_N(&n,&a);
  209. Push_N(&n,a/b);
  210. break;
  211. }
  212. }
  213. Pop_N(&n,&e);
  214. return e;
  215. }/*calculate_postfix*/
  216. int main()
  217. {
  218. char str[M];
  219. char post[M];
  220. int i;
  221. printf("\n输入一串中缀表达式:\n");
  222. gets(str);
  223. printf("\n对应的后缀表达式:\n");
  224. in2post(str,post);
  225. printf("%s",post);
  226. printf("\n\n计算后缀表达式:\n");
  227. printf("%f",cal_post(post));
  228. return 0;
  229. }

运行结果:

五、算法改进

上面的代码正确运算的前提是:

① 输入的中缀表达式合法

② 运算数非负数

 例如 :输入 3*(-6+5) ,得到以下结果,后缀表达式和运算结果明显不正确。

这里的 -6 应该才是一个完整的运算数,但是在后缀表达式中 '-' 号和数字 '6' 被拆分开来了,得到的结果也不正确。因为代码中对于 '-' 号一律是作为操作符处理,所以面对像 -6 这样的负数时不能分析出完整正确的运算数。

  • 如果要进行修改,在进行运算数分析的时候就要考虑负数的情况。当出现 '-' 号的时候,要判断它是作为负数标志,属于运算数的一部分,还是作为一个运算符。

所以需要对运算数分析的代码添加负数的处理,修改后的代码如下:

in2post 函数代码修改:

  1. void in2post(ElemType *str,ElemType *p){ /*infix to postfix*/
  2. //初始化一个空栈
  3. SqStack s;
  4. InitStack(&s);
  5. ElemType e;
  6. int i;
  7. int j=0;
  8. for(i=0 ; i<strlen(str) ; i++) //遍历中缀表达式
  9. {
  10. if('-' == str[i]) //负数情况判断
  11. {
  12. //表达式首位是'-',则一定是作为负数符号
  13. if(0 == i)
  14. p[j++]=str[i++];
  15. //'-'前面是'(',则一定是作为负数符号
  16. else if('(' == str[i-1])
  17. p[j++]=str[i++];
  18. }
  19. //遇到数字和小数点直接输出
  20. while(isdigit(str[i]) || '.'==str[i])
  21. {
  22. p[j++]=str[i++];
  23. if(!isdigit(str[i]) && '.'!=str[i])
  24. p[j++]=' '; //一个数字完整输出后使用空格与其它运算符或数字分隔开
  25. }
  26. //遇到左括号直接入栈
  27. if('('==str[i])
  28. Push(&s,str[i]);
  29. //遇到右括号直接出栈,直到左括号出栈(左括号不输出)
  30. if(')'==str[i])
  31. {
  32. while('(' != *(s.top-1))
  33. {
  34. Pop(&s,&e);
  35. p[j++]=e;
  36. p[j++]=' ';
  37. }
  38. Pop(&s,&e); //左括号出栈但不输出
  39. }
  40. //遇到+或—
  41. //1.栈空/栈顶为左括号:直接入栈
  42. //2.否则一直出栈,直到栈空/栈顶为左括号,再入栈
  43. if('+'==str[i] || '-'==str[i])
  44. {
  45. while(!StackEmpty(&s) && '('!=*(s.top-1)) //栈非空 且 栈顶非左括号
  46. {
  47. Pop(&s,&e);
  48. p[j++]=e;
  49. p[j++]=' ';
  50. }
  51. Push(&s,str[i]);
  52. }
  53. //遇到*或/
  54. //1.栈空/栈顶为左括号/栈顶操作符为+ or -:直接入栈
  55. //2.否则一直出栈,直到满足1,再入栈
  56. if('*'==str[i] || '/'==str[i] || '%'==str[i])
  57. {
  58. while(!StackEmpty(&s) && '('!=*(s.top-1) && '+'!=*(s.top-1) && '-'!=*(s.top-1))
  59. {
  60. Pop(&s,&e);
  61. p[j++]=e;
  62. p[j++]=' ';
  63. }
  64. Push(&s,str[i]);
  65. }
  66. }
  67. //中缀表达式遍历完成,还需检查栈中是否有未输出字符
  68. //判断栈空,非空则直接出栈并输出(左括号不用输出)
  69. while(!StackEmpty(&s)){
  70. Pop(&s,&e);
  71. if('('!=e)
  72. {
  73. p[j++]=e;
  74. p[j++]=' ';
  75. }
  76. }
  77. p[--j]='\0';
  78. }

cal_post 函数代码修改:

  1. double cal_post(char *str){
  2. ElemType2 e,a,b;
  3. char d[M];
  4. //初始化一个运算数栈保存运算数
  5. NStack n;
  6. InitStack_N(&n);
  7. int i=0;
  8. int j=0;
  9. while(str[i]) //遍历后缀表达式
  10. {
  11. switch(str[i])
  12. {
  13. case '-':
  14. if( isdigit(str[i+1]) ) //判断'-'是作为负数符号or运算符
  15. {
  16. d[j++]=str[i++]; //将负号加入运算数字符串
  17. d[j]='\0';
  18. break; //注:这里的break只是跳出switch循环
  19. }
  20. else
  21. {
  22. Pop_N(&n,&b);
  23. Pop_N(&n,&a);
  24. Push_N(&n,a-b);
  25. i++;
  26. break;
  27. }
  28. case '+':
  29. Pop_N(&n,&b);
  30. Pop_N(&n,&a);
  31. Push_N(&n,a+b);
  32. i++;
  33. break;
  34. case '*':
  35. Pop_N(&n,&b);
  36. Pop_N(&n,&a);
  37. Push_N(&n,a*b);
  38. i++;
  39. break;
  40. case '/':
  41. Pop_N(&n,&b);
  42. Pop_N(&n,&a);
  43. Push_N(&n,a/b);
  44. i++;
  45. break;
  46. case ' ':i++;
  47. }
  48. //遇到运算数直接入栈(先转换double类型)
  49. //d保存后缀表达式中的字符串形式的运算数
  50. //使用atof将字符串转换为double类型
  51. while(isdigit(str[i]) || '.'==str[i])
  52. {
  53. d[j++]=str[i++];
  54. d[j]='\0';
  55. if( ' ' == str[i] )
  56. {
  57. e = atof(d); //此时分析出的就是完整的运算数
  58. Push_N(&n,e);
  59. i++;
  60. j = 0;
  61. }
  62. }
  63. }
  64. Pop_N(&n,&e);
  65. return e;
  66. }

  • 运行结果:

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/494872
推荐阅读
相关标签
  

闽ICP备14008679号