首页 > 编程知识 正文

表达式求值是什么应用的典型例子,表达式求值算法

时间:2023-05-04 01:37:14 阅读:22366 作者:4554

计算表达式的基本思想是将具有运算符优先级的算术表达式转换为不具有运算符优先级的后缀表达式以获取值。

三种公式

1 .前缀表达式(波兰表达式) :

前缀表达式是不含括号的数学表达式,在运算符前面写操作数,在操作数后面写操作数。 “3 4”的前缀表达式类似于“3 4”。

2 .骑马订式:

算术表达式,可以包含括号并且运算符位于操作数中间。 “3 4”的骑马订仪式仍然是“3 4”。 骑马订式是一种常用的算术表示方法。

3 .后缀表达式(逆波兰表达式) :

运算符位于两个操作数之后而不包含括号的算术表达式。 例如,“3 4”的后缀表达式为“3 4”。

上述三个表达式可以相互转换,有多种转换方法。 常见的有队列和堆栈、二叉树遍历等,下面介绍前者的方法。

骑马订式-后缀式

操作步骤:

1. 构建一个队列和一个栈;从左向右读取算术表达式,将操作数压入队列,运算符压入栈中。

2. 当读入为运算符时,有以下四种情况:

1)或号,则把栈中的运算符弹出,并压入队列中,直至栈为空或遇到左括号;然后将自己压入栈中。

2)或号,则把栈中的运算符弹出,并压入队列中,直至栈为空或遇到左括号加号减号;然后将自己压入栈中。

3)“)”号,则把栈中的运算符弹出,并压入队列中,直至遇到左括号(将左括号从栈中弹出,但并不将其压入队列)。

4)“(”号,将自己压入栈中。

请注意,在从左向右读完3. 当算术表达式后,请读取堆栈中的所有运算符,然后按顺序将其推入队列。 运算符可以是特殊数字,以便将运算符推入双精度队列。

在上面的三个步骤中,可以将骑马订表达式转换为后缀表达式。 按顺序输出队列中的值可以得到后缀表达式。

例如,如果将"3*(109/2 )-7*2) "转换为后缀表达式,则如下图所示:

得到转换后的后缀表达式后,该如何计算其结果呢?

后缀表达式的评价

骑马订表达式的评估需要考虑运算符的优先级,而后缀表达式则不需要考虑。 后缀表达式的计算严格按照运算符出现的顺序从左到右进行,不再考虑运算符的优先规则。

操作步骤:

1. 构建一个队列和一个栈。队列初始时存储着后缀表达式,栈初始时为空。

2. 从队列中读取元素,若元素为操作数,则弹出队列压入栈中;如果元素为运算符,则再从栈中弹出两个操作数,三者求出结果后,将结果压入栈中。

3. 不断重复第2步,最后栈中的操作数,即为整个算术表达式中的值。

从堆栈的角度来看求后缀表达式“3 10 9 2/ * 7 2 * -”的值的过程吧。

后缀表达式的计算从左到右执行,首先计算“9 2 /”,得到结果后替换原始位置。 也就是说,“3 10 [ 9 2/]”得到“310[4.5]”。 []是发生置换的部分。 运算继续执行,从"3 [ 10 4.5 ] "得到"3 [ 14.5 ] "; 从“[ 3 14.5 * ]”中得到“[ 43.5 ]”; 接着,从" 43.5 [ 7 2 * ] "中得到" 43.5 [ 14 ] "; 最后一步是从“[ 43.5 14 - ]”中得到“[ 29.5 ]”,29.5是最终答案。

实现

以ACM的主题为例,评价公式

# include iostream # include cstdio # includecstdlib # include cstring # include cmath # includevectorusingnamespacestd; int main ()//评估为小数、多位、有符号数、加括号式的计算vectordouble pp、qq、zz; //pp为队列,骑马订后缀和后缀式评价//qq为堆栈,骑马订后缀时堆栈运算符//zz,后缀式评价时

存储操作数 char p[1005]; //存储运算表达式 int i,n; scanf("%d",&n); //输入组数 while(n--) { //中缀表达式转换为后缀表达式 scanf("%s",p); //输入运算表达式 if(strcmp(p,"0")==0) { break; } for(i=0;p[i]!='';i++)//从左向右读取运算表达式 { double s=0; //s表示等待压入队列的操作数 int t=-1,flag=0; //t用于记录紧跟小数点后连续0的个数 loop: while((p[i]>='0'&&p[i]<='9')||p[i]=='.')//读取连续数字或小数点,并将其作为一个操作数 { if(!flag) { flag=1; //表明需要后期处理 } if(p[i]=='.') //发现小数点 { t=0; //准备记录紧跟小数点后0的个数,这里进行初始化 } else { if(t!=-1) { t++; //紧跟在小数点后0的个数加一 } s=s*10+p[i]-'0'; //将字符操作数转为数字操作数 } i++; } if(flag) //后期处理,并将操作数压入队列中 { if(t!=-1) //存在小数点 { s=s*1.0/pow(10,t);//添加小数点 } if(flag==2) { s=-s;//变为负数 } pp.push_back(s); //将操作数压入队列中 i--; //该位为符号,并且未能记录,故在循环的下一次需要记录该位 } else if(p[i]==')') //读到右括号 { //栈中运算符压入队列,直至栈为空,或遇到左括号 while(!qq.empty()&&qq.back()!=-0.1050110) { pp.push_back(qq.back()); //将栈中运算符压入队列中 qq.pop_back(); //将运算符弹出栈 } if(!qq.empty()) { qq.pop_back(); //将左括号弹出栈 } } else if(p[i]=='(') //读到左括号 { qq.push_back(-0.1050110); //将左括号变为数字标记后压入栈 } else if(p[i]=='*'||p[i]=='/') //读到乘号或除号 { //栈中运算符压入队列,直至栈为空,或遇到左括号,加号,减号 while(!qq.empty()&&(qq.back()==-0.1040110||qq.back()==-0.1030110)&&qq.back()!=-0.1050110) { pp.push_back(qq.back()); //将栈中运算符压入队列中 qq.pop_back(); //将运算符弹出栈 } if(p[i]=='*') { qq.push_back(-0.1040110);//将乘号变为数字标记后压入栈 } else if(p[i]=='/') { qq.push_back(-0.1030110);//将除号变为数字标记后压入栈 } } else if(p[i]=='+'||p[i]=='-') //读到加号或减号 { //当该位处于运算表达式第一位或该位前面还有运算符(即该位表示正负) if(i==0||p[i-1]=='+'||p[i-1]=='-'||p[i-1]=='*'||p[i-1]=='/'||p[i-1]=='(') { if(p[i]=='-') { flag=2;//负数标记 } i++; //移向表达式下一位 goto loop; //跳到连续读取数字处 } //栈中运算符压入队列,直至栈为空,或遇到左括号 while(!qq.empty()&&qq.back()!=-0.1050110) { pp.push_back(qq.back()); //将栈中运算符压入队列中 qq.pop_back(); //将运算符弹出栈 } if(p[i]=='+') { qq.push_back(-0.1020110);//将加号变为数字标记后压入栈 } else if(p[i]=='-') { qq.push_back(-0.1010110);//将减号变为数字标记后压入栈 } } } while(!qq.empty())//将栈中所有运算符弹出并按序压入队列 { pp.push_back(qq.back()); qq.pop_back(); } //打印该运算表达式的后缀表达式 /*cout<<"中缀表达式转换为后缀表达式:"; for(i=0;i<pp.size();i++) { if(pp[i]==-0.1040110) { cout<<"*"<<" "; } else if(pp[i]==-0.1030110) { cout<<"/"<<" "; } else if(pp[i]==-0.1020110) { cout<<"+"<<" "; } else if(pp[i]==-0.1010110) { cout<<"-"<<" "; } else { cout<<pp[i]<<" "; } } cout<<endl;*/ //后缀表达式计算结果 for(i=0;i<pp.size();i++) //遍历后缀表达式的队列 { double a,b; if(pp[i]==-0.1010110) //运算符为减号 { a=zz.back(); //从栈中取出第一个操作数 zz.pop_back(); //弹出栈 b=zz.back(); //从栈中取出第二个操作数 zz.pop_back(); //弹出栈 zz.push_back(b-a);//将操作数运算结果压入栈中 } else if(pp[i]==-0.1020110)//运算符为加号 { a=zz.back(); zz.pop_back(); b=zz.back(); zz.pop_back(); zz.push_back(b+a); } else if(pp[i]==-0.1030110)//运算符为除号 { a=zz.back(); zz.pop_back(); b=zz.back(); zz.pop_back(); zz.push_back(b*1.0/a); } else if(pp[i]==-0.1040110)//运算符为乘号 { a=zz.back(); zz.pop_back(); b=zz.back(); zz.pop_back(); zz.push_back(b*a); } else//非运算符 { zz.push_back(pp[i]);//将操作数压入栈中 } } //cout<<"计算结果:"; printf("%.2lfn",zz.back());//栈中的值即为表达式的值 pp.clear();qq.clear();zz.clear();//清理 } return 0;}

使用上面的思路便可以解决很多相似的题目:

简单计算器,体贴的月饼大哥搞算数,前缀式计算



扩展


根据上述的算法思想,并结合Java的Swing包,我编写了一款计算器工具(注释并不齐全,见谅)。




文中如有不恰当之处,还望包容和指出,谢谢


版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。