表达式求值是进行数据处理的最基本操作。请编写程序完成一个简单算术表达式的求值。要求如下:
(1) 运算符包括:+
、-
、*
、-
、^
(乘方)、括号
(2)运算量为数值常量,根据自己的能力可以对运算量做不同的约束,例如1位整数、多位整数、实数等(会有不同的测试用例);
输入:一行,即表达式,以“=”结束。例如:
5*(8-3)+6/5=
输出:一行,即表达式的值。结果值为整数时输出为整数,如果有小数时保留5位小数。
26.20000
输入
5+(3-1)/2=
输出
6
中缀表达式求值基于以下算法:
1.使用两个栈,num用于存储操作数,op用于存储操作符。
2.从左往右扫描,遇到操作数入栈num。
3.遇到操作符时,如果优先级低于或等于栈顶操作符优先级,则从num弹出两个元素进行计算,并压入num,继续与栈顶操作符的比较优先级。
4.如果遇到操作符高于栈顶操作符优先级,则直接入栈op。
5.遇到左括号,直接入栈op,遇到右括号,则直接出栈并计算,直到遇到左括号。
据我推测,本题的测试用例包含一位整数与一位整数、一位整数与多位整数、多位整数与多位整数、一位整数与浮点数、多位整数与浮点数、浮点数与浮点数6种情况。
代码中的一些小细节是在一次次测试中发现的。这个题又折腾了好久。。。
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
template<class T>//模板类
class Stack
{
private:
T a[2005];
int up;
int down;
public:
Stack()
{
up=0;
down=0;
}
void Push(T x)
{
a[up++]=x;
}
void Pop()
{
up--;
}
T Top()
{
return a[up-1];
}
int Size()
{
return up-down;
}
bool Empty()
{
return up==down?true:false;
}
};
Stack<double>num;//double型和char型
Stack<char>op;
char equ[1005];
int prio[130]={0};
void getPriority()//设定优先级,加减<乘除<乘方,同级运算(加与减、乘与除之间)从左往右运算
{
prio['+']=1;
prio['-']=1;
prio['*']=2;
prio['/']=2;
prio['^']=4;
}
int cmp(char theta1,char theta2)//优先级比较
{
return prio[theta1]-prio[theta2];
}
double calculate(double n1,double n2,char op)
{
if(op=='+')return n1+n2;//加
else if(op=='-')return n1-n2;//减
else if(op=='*')return n1*n2;//乘
else if(op=='/')return n1/n2;//除
else if(op=='^')return pow(n1,n2);//乘方
}
int main()
{
int i;
bool flag=false;//表示是否存在小数点
double a,b,sum1=0,sum2=0,t=1,res;
getPriority();
scanf("%s",equ);
int len=strlen(equ);
for(i=0;i<=len-2;i++)
{
if(equ[i]>='0'&&equ[i]<='9')
{
if(flag==false)sum1=sum1*10+equ[i]-'0';//整数部分
else
{
t=t/10;
sum2+=double((equ[i]-'0'))*t;//小数部分
}
}
else if(equ[i]=='.')flag=true;//表示存在小数
else if(equ[i]=='+'||equ[i]=='-'||equ[i]=='*'||equ[i]=='/'||equ[i]=='^'||equ[i]=='('||equ[i]==')')
{
res=sum1+sum2;
if(res!=0)//压栈是有条件的,不能把0压入,否则容易出现除0错误
{
num.Push(res);//遇到运算符表示操作数部分结束,将运算结果压栈
res=0;
sum1=0;
sum2=0;
t=1;
flag=false;
}
if(equ[i]=='(')op.Push('(');//左括号直接压栈
else if(equ[i]==')')//遇到右括号,操作数和运算符出栈并运算,直到遇到左括号为止
{
while(op.Top()!='(')
{
a=num.Top();
num.Pop();
b=num.Top();
num.Pop();
num.Push(calculate(b,a,op.Top()));
op.Pop();
}
op.Pop();
}
else if(op.Empty()||op.Top()=='('||cmp(op.Top(),equ[i])<0)op.Push(equ[i]);//1.栈空 2.栈顶为左括号 3.栈顶运算符优先级小于当前运算符 3种情况下运算符压栈
else if(cmp(op.Top(),equ[i])>=0)//栈顶运算符优先级不小于当前运算符(等于时表示同级运算)
{
while(cmp(op.Top(),equ[i])>=0)//栈顶运算符优先级不小于当前运算符(等于时表示同级运算,恰好能满足同级运算从左到右)
{
a=num.Top();
num.Pop();
b=num.Top();
num.Pop();
num.Push(calculate(b,a,op.Top()));
op.Pop();
if(op.Empty())break;//运算符栈为空,说明操作数栈中只剩一个元素
}
op.Push(equ[i]);//当前运算符已经满足"栈顶运算符优先级小于当前运算符"这个条件,因此压栈
}
}
}
res=sum1+sum2;
if(equ[len-2]!=')')num.Push(res);//尤其注意这里容易漏掉情况
while(!op.Empty())//有些运算符可能留在栈中,因此最后再出栈并运算
{
a=num.Top();
num.Pop();
b=num.Top();
num.Pop();
num.Push(calculate(b,a,op.Top()));
op.Pop();
}
if(num.Top()-int(num.Top())>1e-6)printf("%.5f",num.Top());//小数
else printf("%d",int(num.Top()));//整数
return 0;
}