后缀表达式(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;}}
代码的第一行首先调用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
- 后缀表达式(4)-——中缀表达式到后缀表达式的转换,递归实现
- 中缀表达式到后缀表达式的转换
- 中缀表达式到后缀表达式的转换
- 中缀表达式到后缀表达式的转换
- C++栈的应用——后缀表达式求值、中缀表达式到后缀表达式的转换
- 实现中缀表达式到后缀表达式的转换
- 中缀表达式到后缀表达式的转换C++实现
- 中缀到后缀表达式的转换
- 中缀到后缀表达式的转换:java-stack实现
- 中缀后缀表达式的转换
- 中缀表达式转换到后缀表达式(java实现)
- 表达式 中缀 后缀 转换
- 后缀表达式 (1) ——中缀表达式与后缀表达式的手动转换
- 中缀表达式转换为后缀表达式&后缀表达式的计算
- 中缀表达式转换后缀表达式
- 中缀表达式转换后缀表达式
- 中缀表达式转换后缀表达式
- 中缀表达式转换后缀表达式
- 【Unity闲谈】在Hierarchy中显示Component的图标
- A004-移位范围超过变量宽度-(ques=1)
- 【25.23%】【codeforces 731C】Socks
- 提取计算机当前时间、日期
- JavaEE学习之路—–JSP(三)
- 后缀表达式(4)-——中缀表达式到后缀表达式的转换,递归实现
- 1.4.2 命令式编程范式
- HDU-1026-优先级队列
- 【结论】【树(LCA)】NKOJ3815 树上的询问
- 绝世秘籍之GNU构建系统与Autotool概念分析
- 二进制中1的个数
- Code::Blocks输出中文乱码问题解决方案
- C++学习日记
- sql with as 用法