后缀表达式(4)-——中缀表达式到后缀表达式的转换,递归实现

来源:互联网 发布:清除mysql表碎片 编辑:程序博客网 时间:2024/05/17 21:59

本篇是介绍的是后缀表达式最难,也是最基本的一个问题。即如何将一个中缀表达式转化为后缀表达式。在网上可以找到许多非递归实现的示例代码,它们比本节介绍的代码要简洁且高效,可是不利于理解。本节舍近求远,介绍后缀表达式的递归实现方式,为的是把变换的中心思想解释清楚。


问题描述


输入: 是一串字符串,它表示的是包含加减乘除四则运算以及括号运算的中缀表达式。为了简单起见,我们假设表达式中的运算元都是0到9之间的正整数。这样可以规避词法分析的细节。i


输出 的是一个Element对象组成的数组,表示一个后缀表达式。


主要思想


我们将运算根据优先级分成三组


  • 加法减法运算。它是优先级最低的运算。我们用expr()函数处理。这里expr()函数需要考虑连续加减的情形。
  • 乘法除法运算。它是优先级较高的运算。我们用term()函数处理。这里term()函数需要考虑连续乘除的情形。
  • 括号运算。这是优先级最高的运算。我们在处理的时候,可以把括号内包含表达式看做是一个特殊的运算元,用factor函数处理。factor()函数除了处理括号运算外,也处理由单个数字组成的简单的运算元。


具体实现


expr方法,代码如下

void expr(char* s, int & sn, Element* eles, int& en){term(s, sn, eles, en);while(s[sn] == '+' || s[sn] == '-'){int op = s[sn++];term(s, sn, eles, en);eles[en].is_op = true;eles[en++].val = op;}}


expr方法共有4个参数。s保存的是输入的中缀表达式,它是一个字符串。sn表征最近将要处理的字符的索引。eles数组用来保存最后生成的后缀表达式,而en表示目前已经生成的Element元素个数。


代码的第一行首先调用term方法。这里term()函数的作用是处理最外层非加法和减法运算的表达式。可分为如下三种情况考虑。

  • 如果表达式s中最外层不是加法或者减法运算。那么term()函数会直接把整个表达式处理完成。第4-10行的while循环将不会执行。
  • 如果表达式s可以写成a+b的形式,其中表达式a和b最外层都不是加法或者减法运算。那么第3行的term()函数会首先将表达式a处理完毕。当第3行执行结束后,sn会指向加号‘+’,因此会进入循环。进入循环后,代码第6行首先将加号保存,同时sn++后指向表达式b的第一个字符。这样在代码第7行调用的term()方法会把表达式b处理完毕。当表达式a和b都处理完毕后,我们最后才能把预先保存的加号‘+’运算保存到Element数组末尾。
  • 如果表达式s可以写成a+b-c的形式,其中表达式a,b和c最外层都不是加法或者减法运算。当第3行执行结束后,sn会指向加号‘+’,因此会进入循环。所不同的是,这一次循环会执行两次。具体的细节不再赘述。

term()函数定义与expr()函数完全类似。

void term(char* s, int & sn, Element* eles, int& en){factor(s, sn, eles, en);while(s[sn] == '*' || s[sn] == '/'){int op = s[sn++];factor(s, sn, eles, en);eles[en].is_op = true;eles[en++].val = op;}}

factor()函数需要处理两种情况。

1. 简单的数字;2. 带括号的表达式。

因此在factor()函数的定义中,也分为这两者情况讨论。

void factor(char* s, int & sn, Element* eles, int& en){int op = s[sn++];if(op == '('){expr(s, sn, eles, en);sn++;}else{eles[en].is_op = false;eles[en++].val = op - '0';}}
代码第4-8行处理的是带括号的情形,而代码的第9-13行处理的是普通数字的情形。注意代码第7行,sn++的目的是把右圆括号pass掉。当然,我们这样处理,其实是假设用户输入的中缀表达式总是正确的。


最后附上完整的可执行代码。

#include <stdio.h>typedef struct str_ele{    bool is_op;    int val;} Element;void factor(char* s, int & sn, Element* eles, int& en);void term(char* s, int & sn, Element* eles, int& en);void expr(char* s, int & sn, Element* eles, int& en);int resolve(Element* eles, int& end){int end_t = end--;if(eles[end_t].is_op){int operand2 = resolve(eles, end);int operand1 = resolve(eles, end);switch(eles[end_t].val){case '+': return operand1 + operand2;case '-': return operand1 - operand2;case '*': return operand1 * operand2;case '/': return operand1 / operand2;}}else{return eles[end_t].val;}}void factor(char* s, int & sn, Element* eles, int& en){int op = s[sn++];if(op == '('){expr(s, sn, eles, en);sn++;}else{eles[en].is_op = false;eles[en++].val = op - '0';}}void term(char* s, int & sn, Element* eles, int& en){factor(s, sn, eles, en);while(s[sn] == '*' || s[sn] == '/'){int op = s[sn++];factor(s, sn, eles, en);eles[en].is_op = true;eles[en++].val = op;}}void expr(char* s, int & sn, Element* eles, int& en){term(s, sn, eles, en);while(s[sn] == '+' || s[sn] == '-'){int op = s[sn++];term(s, sn, eles, en);eles[en].is_op = true;eles[en++].val = op;}}void print_elements(Element* eles, int sn){for(int i = 0; i < sn; i++){if(eles[i].is_op)putchar(eles[i].val);else putchar(eles[i].val + '0');}putchar('\n');}int main(){char s[256];Element eles[256];int sn, en;while(scanf("%s", s) != EOF){sn = en = 0;expr(s, sn, eles, en);print_elements(eles, en);printf("%d\n", resolve(eles, --en));}return 0;}



0 0
原创粉丝点击