递归算法实例应用(三)
四则运算表达式求值
Description
给你一个字符串表达式 str
,请你实现一个基本计算器来计算并返回它的值。
注意:不允许使用任何将字符串作为数学表达式计算的内置函数 。
Input
一行,一个四则运算表达式。'*'表示乘法,'/'表示除法
Output
一行,该表达式的值,保留小数点后面两位
Sample Input
样例输入1:
34
样例输入2:
(2+3)*(5+7)+9/3
Sample Output
样例输出1:
34
样例输出2:
63
Tips
1 <= s.length <= 3 * 105
str
由数字、'+'
、'-'
、'('
、')'
组成str
表示一个有效的表达式- '+'和'-'不能用作一元运算(例如, "+1" 和
"+(2 + 3)"
无效) - 输入中不存在两个连续的操作符
- 每个数字和运行的计算将适合于一个有符号的32位整数
算法思想:
通过对题目的分析知道该题为递归形式定义的问题,其递归形式可描述如下:
所以,从递归定义出发,本题应分为三部分进行讨论,即表达式、项、因子,因为这三者相互嵌套组成了输入的一个表达式。
- 表达式,可以是一个单独的项,也可以由 项1 op 项2 组成。
- 项,可以是一个单独的因子,也可以由 因子1 op 因子2 组成。
- 因子,可以是一个单独的整数,也可以由 ( 表达式 ) 组成。
三者相互嵌套,递归表达构成了一个四则运算表达式。递归的终止条件为3中由整数形式定义的因子。
所以我们可以通过三个函数来实现对表达式、项、因子的求值。
代码逻辑:
- 一次性读取输入的表达式,
对于“表达式函数”而言有两种情况:
- 该表达式为一个单独的项,此时仅需通过“项函数”读取改项的值,并作为结果返回即可
- 若该表达式为 项1 op 项2,此时op应为‘+’或‘-’,先从输入缓冲区中消耗掉该op运算符,然后再读取下一个项的值,再做对应操作符计算即可。
- 代码逻辑为:
int Expression_Value() { int result = Term_Value(); //取项1的值 while (true) { char operation = peek(); //查看输入流中下一个字符 if (operation == '+' || operation == '-') { getchar(); //消耗掉输入流中下一个的'+'或'-'运算符 int nextTerm_Value = Term_Value();//若操作符为+或-,则应有项2,记录项2 if (operation == '+') { result += nextTerm_Value;//更新结果为项1+项2 } else { result -= nextTerm_Value;//更新结果为项1-项2 } } else {//若输入流中下一个字符不为+或-则表明该表达式为单项组成 break; } } return result; }
对于“项式函数”有两种情况:
- 该项为一个单独的因子,此时仅需通过“因子函数”读取该因子的值,并作为结果返回即可
- 若该项为 因子1 op 因子2,此时op应为‘*’或‘/’,先从输入缓冲区中消耗掉该op运算符,然后再读取下一个表达式的值,再做对应操作符计算即可。
- 代码为:
int Term_Value() { int result = Factor_Value();//取因子1的值 while (true) { char operation = peek();//查看输入流中下一个字符 if (operation == '*' || operation == '/') { getchar();//消耗掉这个'*'或'/'运算符 int nextFactor_Value = Factor_Value();//若操作符为*或/,则应有因子2,记录因子2 if (operation == '*') { result *= nextFactor_Value;//更新结果为因子1*因子2 } else { result /= nextFactor_Value;//更新结果为因子1/因子2 } } else {//若输入流中下一个字符不为*或/则表明该项为单因子组成 break; } } return result; }
对于“因子函数”同样也有两种情况:
- 该因子为一个单独的整数因子,此时仅需将因子字符串转换成对应的整数类型,并作为结果返回即可。
- 若该项为 ( 表达式 ),此时因子由表达式递归而成,而表达式被左右括号包裹着,所以我们应先从输入缓冲区中消耗掉左括号,然后调用“表达式函数”求该表达式的值,并将该表达式的值作为结果返回。
- 其中关于字符串转整形数据,可按照秦九韶算法思想进行转换,比较简单,不再阐述,读者自行模拟一遍即可领会。
- 代码为:
int Factor_Value() { int result = 0;//记录这个因子的结果 char ch = peek();//查看输入流中下一个字符 if (ch == '(') {//若下一个字符为(,则表明该因子为'(因子)'的形式 getchar();//消耗掉'(' result = Expression_Value(); getchar();//消耗掉')' } else {//若下一个字符不为(,则表明该因子为整数形式,所以根据将字符转化为整形数字 while (isdigit(ch)) {//如‘2’‘7’,result=(10*0+2)*10+7=2*10+7 result = 10 * result + (ch - '0'); getchar();//消耗掉该位字符。 ch = peek(); } } return result; }
关于peek()函数的一点说明:
- 该方法为编者自定义的一个函数,主要实现C++中int peek()函数功能。即返回在输入流中的下一个字符或如果是处于被入的文件的结尾处返回EOF。
- peek()函数不会把字符从流中移除,也不改变字符所处位置。
- 代码为:
int peek() { int ch = getchar();//读入一个字符 ungetc(ch, stdin);//将该字符重新放入该字符在输入缓冲区中所处位置 return ch; }
代码整合:
#include <stdio.h>
#include <stdbool.h>
#include <ctype.h>
/* 表达式求值题目函数声明 */
int Expression_Value(); //求一个表达式的值
int Term_Value(); //求一个项的值
int Factor_Value(); //求一个因子的值
int main() {
printf("%d\n", Expression_Value());//输出表达式的值。
return 0;
}
int peek() {
int ch = getchar();//读入一个字符
ungetc(ch, stdin);//将该字符重新放入该字符在输入缓冲区中所处位置
return ch;
}
//计算表达式的值
int Expression_Value() {
int result = Term_Value(); //取项1的值
while (true) {
char operation = peek(); //查看输入流中下一个字符
if (operation == '+' || operation == '-') {
getchar(); //消耗掉输入流中下一个的'+'或'-'运算符
int nextTerm_Value = Term_Value();//若操作符为+或-,则应有项2,记录项2
if (operation == '+') {
result += nextTerm_Value;//更新结果为项1+项2
} else {
result -= nextTerm_Value;//更新结果为项1-项2
}
} else {//若输入流中下一个字符不为+或-则表明该表达式为单项组成
break;
}
}
return result;
}
//计算项的值
int Term_Value() {
int result = Factor_Value();//取因子1的值
while (true) {
char operation = peek();//查看输入流中下一个字符
if (operation == '*' || operation == '/') {
getchar();//消耗掉这个'*'或'/'运算符
int nextFactor_Value = Factor_Value();//若操作符为*或/,则应有因子2,记录因子2
if (operation == '*') {
result *= nextFactor_Value;//更新结果为因子1*因子2
} else {
result /= nextFactor_Value;//更新结果为因子1/因子2
}
} else {//若输入流中下一个字符不为*或/则表明该项为单因子组成
break;
}
}
return result;
}
//计算因子的值
int Factor_Value() {
int result = 0;//记录这个因子的结果
char ch = peek();//查看输入流中下一个字符
if (ch == '(') {//若下一个字符为(,则表明该因子为'(因子)'的形式
getchar();//消耗掉'('
result = Expression_Value();
getchar();//消耗掉')'
} else {//若下一个字符不为(,则表明该因子为整数形式,所以根据将字符转化为整形数字
while (isdigit(ch)) {//如‘2’‘7’,result=(10*0+2)*10+7=2*10+7
result = 10 * result + (ch - '0');
getchar();//消耗掉该位字符。
ch = peek();
}
}
return result;
}
By Ss1Two 2023/01/16