算术表达式求值
[问题描述] 一个算术表达式是由操作数(operand)、运算符(operator)和界限符(delimiter)组成的。假设操作数是正实数,运算符只含加减乘除等四种运算符,界限符有左右括号和表达式起始、结束符“#”,如:#(7+15)*(23-28/4)#。引入表达式起始、结束符是为了方便。编程利用“运算符优先法”求算术表达式的值。
解题思路:
1.首先建立两个栈用来分别存储操作数(operand)以及运算符(operator),为了减少代码量就直接调用STL中的栈方法来对“入栈”“出栈”以及“访问栈顶元素”进行操作。
2.最重要的一点是“如何才能使表达试正确遵循运算法则”(先乘除后加减,先算括号内的再处理括号外的),这就涉及到运算符优先级的问题,即在 Precede()方法中用二维数组定义好运算符的优先关系,然后根据方法返回的结果进行不同的操作。
3.最后是关于输入问题,操作数和运算符都只用字符型来输入,然后通过字符的ASCII码值来区分是是操作数还是运算符,然后正确进栈。
下面直接上代码,代码中有详细解释
#include<iostream>
#include<stack>
#include<cmath>
using namespace std;
stack<char>OPTR;
stack<double>OPND;
typedef struct {//因为在输入表达式时是一个一个的输入字符,而操作数若长度大于2则无法正确得到操作数,因此该结构是用来存储一个完整的字符数,便于在CharTOInt()中将其转换为数字
char data[10];
int length;
}haha;
bool In(char ch)//判断输入的是不是运算符,若是则返回true
{
if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' || ch == ')'||ch=='#')
{
return true;
}
else return false;
}
char Precede(char x, char y)//运算符优先级的判断
{
int ang[2];
char opnd[2] = { x,y };
char aa[7][7] = {
{'>','>','<','<','<','>','>'},
{'>','>','<','<','<','>','>'},
{'>','>','>','>','<','>','>'},
{'>','>','>','>','<','>','>'},
{'<','<','<','<','<','=',' '},
{'>','>','>','>',' ','>','>'},
{'<','<','<','<','<',' ','='}
};
for (int z = 0; z < 2; z++)
{
switch (opnd[z])
{
case '+':ang[z] = 0;
break;
case '-':ang[z] = 1;
break;
case '*':ang[z] = 2;
break;
case '/':ang[z] = 3;
break;
case '(':ang[z] = 4;
break;
case ')':ang[z] = 5;
break;
case '#':ang[z] = 6;
break;
}
}
return aa[ang[0]][ang[1]];
}
double Operate(double a, char theta, double b)//加减乘除运算
{
double result;
switch (theta)
{
case '+': result=a + b;
break;
case '-':result= a - b;
break;
case '*':result= a * b;
break;
case '/':result= a / b;
break;
}
return result ;
}
double charTOint(haha cunchu)//将字符数字转换成一个完整的数字型数字
{
int point=0;//用来判断是否有小数点以及小数点的位置
double result=0;
for (int i = 0; i < cunchu.length; i++)//先找出小数点的位置
{
if (cunchu.data[i] == '.')
break;
++point;
}
for (int i=0,j = 0; i < cunchu.length; i++)//根据结构中的length以及小数点位置可以正确转换数字
{
if (i != point)//小数点不放入计算
{
result += (cunchu.data[i] - '0')*pow(10, point - j - 1);
j++;
}
}
return result;
}
double EvaluateExpression()//最关键的方法
{
bool flag = true;//用来判断是否输入一个数字完毕,输入完毕则转化为数字型,存入栈中
haha cunchu;//用来记录每个数字字符,然后转换为数字计算
OPTR.push('#');//将表达式起始符压入栈中,用于判断是否该结束循环
char ch;
int i=0;
char theta;//记录弹出来的运算符
double a, b;//记录弹出来的操作数
cout<<"请输入正确的表达式,以'#'结束"<<endl;
cin >> ch;
cunchu.length = 0;
while (ch != '#' || OPTR.top() != '#')//就算ch=='#'也不一定停止循环,因为OPTR中可能还有加减乘除需要运算
{
if (!In(ch))
{
cunchu.data[i] = ch;
++i;
++cunchu.length;
flag = true;
cin >> ch;
}
else {
if (flag)//这判断非常重要,是操作数正确进栈关键方法。思路:即当输入运算符时,表明运算符的前一个操作数已经输入完毕,此时可以将结构存储的数字字符转换成double型,并将数字进入OPND栈
{
OPND.push(charTOint(cunchu));//字符转换数字
cunchu.length = 0; i = 0;//所有回到初始化,用以记录下一个数字
flag = false;//操作数进栈成功后必须将flag取反,因为当操作数进栈后,它在switch中,若进行case '>'时,此时的ch操作符是还没进栈的,ch还需再次进入while循环,判断ch与OPTR栈顶操作符比较优先级,
//再决定ch的情况。正因为ch还需进入while循环,所以在此之前需将flag=false,否则flag=true,又会执行OPND.push(charTOint(cunchu)),这时是将数字0进栈,将会导致运算出错
}
switch (Precede(OPTR.top(), ch))
{
case '>': { theta = OPTR.top();OPTR.pop(); //如果栈顶运算符优先级较大,则取OPND栈顶两个数以及OPTR栈顶运算符进行运算,结果再入栈
b= OPND.top(); OPND.pop();
a= OPND.top(); OPND.pop();
OPND.push(Operate(a, theta, b));//此处不用输入ch,是因为原值还没入栈,其优先级比较小,仍要跟下一个栈顶比较
break;
}
case '<': {
OPTR.push(ch);
cin >> ch;
break;
}
case '=': {
OPTR.pop();
cin >> ch;
break;
}
}
}
}
return OPND.top();
}
int main()
{
cout<<"结果:" EvaluateExpression();
}
运行结果
小白,第一篇博客勿喷。。。
来源:CSDN
作者:鸭绒
链接:https://blog.csdn.net/weixin_43334673/article/details/103593409