(包含“+ - * / ^ ()”)
这次的程序目的是要将一个中缀表达式转化为后缀表达式并计算,我用两个栈来实现这个功能,一个记录操作符,一个记录操作数,并采取一边转化一边计算的方法最后得出结果。但在这之前,必须先解决以下几个问题。
(1)当遇见“+ - * / ^”操作符时,压人栈之前,什么情况下该从栈里提取操作符计算;而且情况可能很复杂,很可能会导致代码过于冗长。
(2)当有括号时,由于括号的优先级最高,这时该如何处理;
(3)当遇到等号时,很可能符号栈里还有一些(一个或以上)操作符并没有处理,这时该如何判断。
(一)为解决第一个问题,我想了一个办法,就是写一个用来判断符号优先级的函数,我写这个函数是用来解决什么时候该从栈里提取操作符计算的,这里又有几点要说明的;
(1)当遇见"+”“-"则从栈顶是“+ - * / 还是 ^”都应该要提取出来并进行计算。所以这时函数应把这种情况判为真。
(2)当遇见"-”“/"则只有栈顶是“ * / ^”时才提取出来并进行计算且函数应把这种情况判为真。其他判为假。
(3) 当遇见"^”因为“^”优先级高,而且如计算2^2^3,应先算后面,所以这时无论栈顶是“+ - * / 还是 ^”都不应该计算先,而应该先暂时把"^"压人栈,所以这时函数应把这种情况判为假。
(4)当遇见其他操作符时,无需进行优先级判断。或者若栈顶元素是其他操作符时,函数则将此种情况判错。
还有为解决情况太多导致代码过长的问题,我也专门写了一个用来计算并能把并计算结果压人操作数栈的函数。
(二)对于第二个问题,只需在遇到“(”时将其压人栈,无需处理,等在当遇到“)”时才将“(”之上的操作符按从上到下依次提取出来并计算,且将“(”弹出栈即可
(三)当遇到“=”时,这时认为表达式结束,则与遇到括号类似,将栈里的符号按从上到下提取出来并计算,这时遇到的另外一个问题就是如何判断符号已经被提取完毕了,我认为这是必须的,因为提取完了还进行提取的话很有可能就会导致程序出错(内存泄露)不过这也很容易解决,只需要在程序刚开始时将"0"压人符号栈来进行接下来的判断即可。
分析完毕后,下面就是我写的程序:
#include<iostream>
using namespace std;
#include<stack>
#include<string>
#include<cmath>
bool prev(string s1,string s2 ){ //判断符号s1 与 s2 的优先级别的函数
if(s1=="^"||s1=="*"||s1=="/")
return true;
else if((s1=="+"||s1=="-")&&(s2=="+"||s2=="-"))
return true;
else
return false;
}
void result(string st,stack<double> &d){ // 计算结果的函数
double x=d.top ();d.pop();
double y=d.top ();d.pop() ;
double re;
if(st=="+")
re=y+x;
else if(st=="-")
re=y-x;
else if(st=="*")
re=y*x;
else if(st=="/")
re=y/x;
else if(st=="^")
re=pow(y,x);
cout<<y<<st<<x<<"="<<re<<endl;
d.push(re);
}
int main(){
string opr,st; //opr 为操作数或操作符,st 用来提取符号栈的符号
stack<double> d; //用来记录计算结果的栈
stack<string> s; //用来记录符号的栈
s.push("0"); //用来判断符号栈里是否还有还有符号,防止出错
double x; //若opr为操作数,则用x表示
while (cin>>opr)
{
if(opr=="+"||opr=="-"||opr=="*"||opr=="/")
{
st=s.top();
while(prev(st,opr))
{ result(st,d);
s.pop();
st=s.top();
}
s.push (opr);
}
else if(opr=="^"||opr=="(")
s.push(opr);
else if(opr==")")
{
st=s.top();
while(st!="(")
{result(st,d);
s.pop();
st=s.top();
}
s.pop();
}
else if(opr=="=")
{ st=s.top();
while(st!="0")
{result(st,d);
s.pop();
st=s.top();
}
cout<<d.top()<<endl;
d.pop();
}
else
{
x=atof(const_cast<const char *>(opr.c_str()));//类型转换
d.push(x);
}
}
return 0;
}
案例:
case1:1 - 0.5 ^ 2 ^ 0 + ( 2 - 1 ) =
2^0=1
0.5^1=0.5
1-0.5=0.5
2-1=1
0.5+1=1.5
1.5
case2:2 + ( 0.5 + ( 2 - 1 ) ^ 2 * 2 ) * 0.5 =
2-1=11^2=1
1*2=2
0.5+2=2.5
2.5*0.5=1.25
2+1.25=3.25
3.25
case3:( 1 + ( 2 / 3 ) ^ 2 ^ 0 ) * 2 =
2/3=0.6666672^0=1
0.666667^1=0.666667
1+0.666667=1.66667
1.66667*2=3.33333
3.33333
通过上面三个案例,经检验,无论是计算次序还是计算结果都没有错,说明我的设计是正确的,但我的程序有两个比较大的缺陷
(1)没有设置判断输入表达式错误的情况,所以必须给出正确的表达式才能进行正确的计算,如给出错误的表达式,本应该给出报错的,但还是会进 行计算,可惜因为输入情况错误的情况过多,判断起来过于复杂,所以我就没有进行判断。
(2)输入的时候太麻烦,因为每次输入一个操作符或操作数时,都要空格,这是因为string的限制,我暂时也没有想到好的办法解决这个问题
不过,我认为我的程序也有一个很大的优点,那就是易于推广,如要进行对数或三角数的计算,只需加入"log" "sin"等符号并判断优先级 别并用相应的计算方法即可,并不会导致程序过于复杂。