所谓表达式求值,就是写一个微型计算器。例如输入:(1+9)* 2 / 2 - 1,输出计算结果9。对于这样的问题,我们一般利用栈,模拟数学运算来完成。为了简化问题,在继续下面的分析之前,先在此作个约定:本文只讨论+-*/()
基本的四则运算,另外不对意外出现的符号(例如^)和不符合规范的数学表达式(例如2*-1)做异常处理。
我们用一个字符数组(即char s[1000]
)来存储数学表达式,定义一个全局变量g_pos表示s[ ]的下标,下标从0开始。首先我们定义两个栈,optr和opnd,分别存储运算符和运算数,遇到运算数直接放进opnd;遇到运算符,分四种情况:
(1):遇到负号;
(2):遇到右括号;
(3):遇到左括号;
(4):遇到+-*/;
前三种情况容易理解,这里就谈下第四种情况。我们都知道四则运算是遵循先乘除再加减的,因此对于每个运算符,我们都要和optr的栈顶符号等级比较,如果这个符号的等级比optr栈顶符号等级高,什么也不做,直接放进栈;如果小于等于,就需要把opnd的栈顶两个数字抽出来,进行计算。这里有个问题,在遇到第一个运算符时,此时optr为空,而我们又需要与optr栈顶运算符比较等级,这时候怎么办?为了解决这个问题,我们在初始化optr的时候,放进一个#,设置其等级为最低。
看到这里你可能还存有一个疑问,如何判定符号-
是负号还是减号?例如-1+2 和 5-1+2。分析发现,出现负号只有两种情况:
(1):左边是左括号,例如(-1+5*3);
(2):字符串的第一个字符,例如-5*6-1。
为了方便代码书写,若出现负号,就在运算数栈加入一个数字0(这也就是下面代码里出现bool值is_minus的原因),即转化一下表达式,例如-1+2转化为0-1+2。
/**
*
* author 刘毅(Limer)
* date 2017-02-24
* mode C++
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <stack>
#include <cmath>
using namespace std;
char s[1000];
int g_pos; // 字符数组的下标
/* 字符转数字 */
double Translation(int & pos)
{
double integer = 0.0; // 整数部分
double remainder = 0.0; // 余数部分
while (s[pos] >= '0' && s[pos] <= '9')
{
integer *= 10;
integer += (s[pos] - '0');
pos++;
}
if (s[pos] == '.')
{
pos++;
int c = 1;
while (s[pos] >= '0' && s[pos] <= '9')
{
double t = s[pos] - '0';
t *= pow(0.1, c);
c++;
remainder += t;
pos++;
}
}
return integer + remainder;
}
/* 返回运算符级别 */
int GetLevel(char ch)
{
switch (ch)
{
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
case '(':
return 0;
case '#':
return -1;
};
}
/* 对两个数进行运算 */
double Operate(double a1, char op, double a2)
{
switch (op)
{
case '+':
return a1 + a2;
case '-':
return a1 - a2;
case '*':
return a1 * a2;
case '/':
return a1 / a2;
};
}
/* 利用两个栈进行模拟计算 */
double Compute()
{
stack<char> optr; // 操作符栈
stack<double> opnd; // 操作数栈
optr.push('#');
int len = strlen(s);
bool is_minus = true; // 判断'-'是减号还是负号
for (g_pos = 0; g_pos < len;)
{
//1. 负号
if (s[g_pos] == '-' && is_minus) // 是负号
{
opnd.push(0);
optr.push('-');
g_pos++;
}
//2. 是右括号 )
else if (s[g_pos] == ')')
{
is_minus = false;
g_pos++;
while (optr.top() != '(')
{
double a2 = opnd.top();
opnd.pop();
double a1 = opnd.top();
opnd.pop();
char op = optr.top();
optr.pop();
double result = Operate(a1, op, a2);
opnd.push(result);
}
optr.pop(); // 删除'('
}
//3. 数字
else if (s[g_pos] >= '0' && s[g_pos] <= '9')
{
is_minus = false;
opnd.push(Translation(g_pos));
}
//4. ( 左括号
else if (s[g_pos] == '(')
{
is_minus = true;
optr.push(s[g_pos]);
g_pos++;
}
//5. + - * / 四种
else
{
while (GetLevel(s[g_pos]) <= GetLevel(optr.top()))
{
double a2 = opnd.top();
opnd.pop();
double a1 = opnd.top();
opnd.pop();
char op = optr.top();
optr.pop();
double result = Operate(a1, op, a2);
opnd.push(result);
}
optr.push(s[g_pos]);
g_pos++;
}
}
while (optr.top() != '#')
{
double a2 = opnd.top();
opnd.pop();
double a1 = opnd.top();
opnd.pop();
char op = optr.top();
optr.pop();
double result = Operate(a1, op, a2);
opnd.push(result);
}
return opnd.top();
}
int main()
{
while (cin >> s)
cout << "结果为:" << Compute() << endl << endl;
return 0;
}
数据测试如下图: