问题描述:
输入为四则运算表达式,仅由整数、+、-、*、/、(、)组成,没有空格,要求求其值。假设运算符结果都是整数。“/”结果也是整数。
样例输入:
(2+3)*(5+7)+9/3
样例输出:
63
问题的思考及解决:
需要考虑如下几个问题:
1)计算的优先级如何实现
2)用哪一种算法的思路进行实现
3)数字与运算符混合输入,如何进行区分
我们将进行如下的考虑:
1)计算的优先级分别为:() ;* / ; 最后的是+-。如果没有()运算符,则直接进行+-*/四则运算就可以了。我们可以发现,有3层的运算优先级,且()这个运算符内可能又是一个长长的运算公式,里面可能还套个()。在这种情况下,我们不妨把问题简单化,即先考虑只有+-的运算,然后考虑+-和*/同时存在的运算,最后考虑+-*/()同时存在的运算,即我们要解决的问题。
2)解决问题的思路,就要由易向难进行处理,直接去处理3个运算顺序显得有些难度,那么我们只考虑计算+-的算式。
输入公式:11+22+33-11
输出参数:55
在最简单的问题上,我们只需要考虑如何将数据进行录入,符号如何相加减即可。在这个问题上,我们可以考虑用cin.get()和cin.peek()这2个函数,以及isdigit()判定数字的函数进行辅助。
在算法上,我们考虑定义2个函数,其中一个函数factor_value()用于读取数字并返回当前的数字,另外一个函数expression_value()用于处理+-运算。
函数factor_value()的实现思路:读取第一个字母,判断是否为数字,如果为数字,继续读取下一个字母。当前的数值为第一个数字的值*10再加上第二个数字的值,依次类推,直至读取的字母不为数字停止,返回当前读取的数字。
函数expression_value()的实现思路:通过调用factor_value()获取数字,读取下一个字母,如果为+-符号,则与下一个读取的数字进行计算。
加减运算的表达式求解代码如下:
#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;
int factor_value();
int expression_value();
void main()
{
cout << "Value:" << expression_value();
}
int expression_value()
{
int value = factor_value();
char ch = cin.peek();
while (true)//循环始终进行,如果不是+-数字则退出
{
ch = cin.get();
if (ch == '+' || ch == '-')
{
if (ch == '+')
value += factor_value();
else
value -= factor_value();
}
else
break;
}
return value;
}
int factor_value()
{
char ch=cin.peek();
int value = 0;
while (isdigit(ch))
{
cin.get();//如果上一步循环中的peek的参数为数字,则get一下,cin指向下一个
value = value * 10 + ch - '0';
ch = cin.peek();//这个细节要注意,先peek一下看看,如果在while循环判断不是数字,则直接跳出
}
return value;
}
接着再把问题的难度上去一层,即把*/运算与+-运算一块综合起来进行考虑。
这个时候就要考虑运算符的优先程度了。如果存在*/运算,那么必然要比+-运算的优先级高,优先进行*/运算。由于同级别的顺序运算已经在上一步中完成了相关的思路梳理,这个阶段,最困难的就是如何处理+-运算与*/运算优先计算的问题,用什么方式进行处理。
在这个问题的实现上,我们仍需要再定义一个函数term_value(),这个函数用于处理*/计算。在处理优先级的问题上,我们需要考虑使用递归的思想进行处理该问题。在最简单的问题上,我们处理的字符可以划分为2类,+-运算符和数字。当出现*/时,我们同样以该思想考虑问题,即两个数字相乘的表达式,我们把这个表达式当作一个数字来处理,那么我们就可以把上面代码中,expression_value()函数中的factor_value()修改成term_value(),实现term_value()的内容与实现+-运算的方式相同。
加减运算的表达式求解代码如下:
#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;
int factor_value();
int term_value();
int expression_value();
void main()
{
cout << "Value:" << expression_value();
}
int expression_value()
{
int value = term_value();
while (true)//循环始终进行,如果不是+-数字则退出
{
char ch = cin.peek();
if (ch == '+' || ch == '-')
{
cin.get();
if (ch == '+')
value += term_value();
else
value -= term_value();
}
else
break;
}
return value;
}
int term_value()
{
int value = factor_value();
while (true)//循环始终进行,如果不是+-数字则退出
{
char ch = cin.peek();//在读取字符的部分,进行了部分修改
if (ch == '*' || ch == '/')
{
cin.get();
if (ch == '*')
value *= factor_value();
else
value /= factor_value();
}
else
break;
}
return value;
}
int factor_value()
{
char ch=cin.peek();
int value = 0;
while (isdigit(ch))
{
cin.get();//如果上一步循环中的peek的参数为数字,则get一下,cin指向下一个
value = value * 10 + ch - '0';
ch = cin.peek();//这个细节要注意,先peek一下看看,如果在while循环判断不是数字,则直接跳出
}
return value;
}
上述代码微调了读取字母部分的代码,否则无法正常运行,因为在只有+-的代码中,函数只有1层的调用关系。而在2种不同的计算优先级代码中,需要有2层的调用关系,如果直接通过cin.get(),则会造成字符被提前调出的问题。读者在这个细节上自习研究一下,这个小细节值得细细品味。
最后再把括号的计算优先级加入其中。这时会发现,括号的计算又和+-,*/这2种计算有所不同,再多一个计算,例如^求幂,则可以通过类似的方式,再多加一重函数关系即可处理该问题,但是括号与之不同点在于括号内部又是一个算式。关于括号内的算式如何进行计算,又是一个问题。我们依然把问题简化,假设括号里面是没有括号进行嵌套的。那么,括号内的算式需要进行+-*/四则运算,该运算的函数我们已经在第二个代码中写了,即调用expression_value()函数。此时,我们要考虑一个问题,是不是还需要再新建一个函数,用于判断括号。如果新建一个函数,我们会发现,其实括号的运算和数字其实是一样计算顺序,因为我们可以把括号内的算式的结果当作是数字来考虑。既然是同样的等价,那么我们把括号的判断放入函数factor_value()中进行。此时我们会发现定义的3个函数是循环调用的,且调用的终止条件为出现数字时。
最终代码:
#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;
int factor_value();//求一个数值或括号内的值,即因子值
int term_value();//求*/的值,即项的值
int expression_value();//求+-的值,即算式的值
void main()
{
cout << "Value:" << expression_value();
}
int expression_value()
{
int value = term_value();
while (true)//循环始终进行,如果不是+-数字则退出
{
char ch = cin.peek();//在读取字符的部分,进行了部分修改
if (ch == '+' || ch == '-')
{
cin.get();
if (ch == '+')
value += term_value();
else
value -= term_value();
}
else
break;
}
return value;
}
int term_value()
{
int value = factor_value();
while (true)//循环始终进行,如果不是+-数字则退出
{
char ch = cin.peek();
if (ch == '*' || ch == '/')
{
cin.get();
if (ch == '*')
value *= factor_value();
else
value /= factor_value();
}
else
break;
}
return value;
}
int factor_value()
{
char ch = cin.peek();
int value = 0;
if (ch == '(')
{
cin.get();//跳过(,取(后一个字符的值
value = expression_value();
cin.get();
}
else
{
while (isdigit(ch))
{
cin.get();//如果上一步循环中的peek的参数为数字,则get一下,cin指向下一个
value = value * 10 + ch - '0';
ch = cin.peek();//这个细节要注意,先peek一下看看,如果在while循环判断不是数字,则直接跳出
}
}
return value;
}