表达式求值专题总结!

来源:互联网 发布:ios 知乎 编辑:程序博客网 时间:2024/06/17 06:05

 很久就有这个打算要搞一个表达式求值专题了,这个一直是省赛必出题,也是我们的薄弱项。队友任命我负责这个专题,真是荣幸!奈何虚拟OJ上找不到相关的专题,于是把NYOJ上几道很水的表达式求值做完了,就算完成了吧,再难我也不会啊!

 表达式求值通用解法:开两个栈,一个符号栈(char),一个数值栈(double/int);根据算术运算符的优先级来决定运算先后从而得到正确答案。

 来认识一下算术运算符:基本四则运算符:‘+’、‘-’、‘*’、‘/’。小学老师教过,先乘除再加减,如果只是简单的四则运算的话很容易知道当前要入栈的符号的优先级不大于符号栈顶元素的优先级的话先将已入栈的元素进行运算再进入栈操作。最终运算只剩一个数就是结果。

 通俗解法:如果是数字,将数字入栈,如果是符号,判断符号栈顶元素的优先级与当前符号优先级关系,再进行操作。如果题目所涉及的运算符只有很基本或者很简单的几种,那就没有这么多优先级的关系了,碰上运算符就直接运算。但如果涉及到比较复杂的运算符,也就是题目也许不止这四种基本运算符还有‘%’,或者有位运算符,那就另当别论了。特别需要注意的一点是括号的优先级最高,所以我一般的做法是先将优先级高的运算完,优先级最低的最后运算,因为优先级最低从左往右或从右往左都一样比如‘+’。那把优先级高的运算符优先运算,但括号的优先级又是最高的,怎么判呢,我们可以特判‘)’,其他的符号还是照常入栈。当出现‘)’,说明必定有与其对应左括号,但我们之前已经将优先级高的先运算完了,括号里的只剩下优先级低的了,我们只需扫一遍过去把括号消除就好了,但特别要注意边界问题。一般这个地方处理不好很容易导致RE等错误出现。

  下面就NYOJ几道表达式求值由简到难作个归纳:

  表达式求值   

时间限制:3000 ms  |  内存限制:65535 KB
难度:3
  题意:给出三种基础表达式,add(x,y)-求和;min(x,y)-求最小值;max(x,y)-求最大值。其中表达式可以嵌套使用。求最终结果,题目保证输入合法。

 思路:由于三种运算优先级一样,所以策略是遇到右括号直接运算,我们可以用add、min、max的第三个字符作为标记。

const int N=1e4+10;char s[N];stack<int>q1;stack<char>q2;void get_num(int &i){    int num=0;    while(isdigit(s[i]))    {        num=num*10+(s[i]-'0');        i++;    }    i--;    q1.push(num);}void calu(){   char c=q2.top();   q2.pop();   if(c=='(') return ;   int a=q1.top();   q1.pop();   int b=q1.top();   q1.pop();   int ans=0;   if(c=='x') ans=max(a,b);   else if(c=='n') ans=min(a,b);   else if(c=='d') ans=a+b;   q1.push(ans);}int main(){    int t;    scanf("%d",&t);    while(t--)    {        while(!q1.empty()) q1.pop();        while(!q2.empty()) q2.pop();        scanf("%s",s);        int len=strlen(s);        for(int i=0; i<len; i++)        {            if(isdigit(s[i])) get_num(i);            else if(s[i]==')')            {                calu();              if(!q2.empty()&&q2.top()=='(') q2.pop();//遇到括号直接运算            }            else            {               if(s[i]=='a'||s[i]=='m')               {                   q2.push(s[i+2]);                   i+=3;               }               else if(s[i]!=',') q2.push(s[i]);            }        }        while(!q2.empty()) calu();        printf("%d\n",q1.top());    }    return 0;} //5//add(1,2)//max(1,999)//add(min(1,1000),add(100,99))//add(max(1,1000),add(100,99)) /**************************************************************    Problem: 1304    User: 1506915059    Language: C++    Result: 正确    Time:0 ms    Memory:1716 kb****************************************************************/

  

表达式求值

时间限制:1000 ms  |  内存限制:65535 KB
难度:3
  题意:给你只包含‘+’和‘*'以及smax()的表达式。函数 Smax(X,Y)也是表达式,其值为:先分别求出 X ,Y 值的各位数字之和,再从中选最大数。表达式可以嵌套,求最终结果。

 思路:这个题其实要比上一个题更好写的,这题只包含三种运算,而且优先级分明,我们说过,括号的优先级最高,所以Smax必定和括号同时出现,我们只需按先乘法再加法的规则算出括号内的再去括号就行了。加法的优先级最低,所以我们每次出现乘法或者’)‘直接开始运算。

char s[N];stack<int>q1;stack<char>q2;void get_num(int &i){    int num=0;    while(isdigit(s[i]))    {        num=num*10+(s[i]-'0');        i++;    }    i--;    q1.push(num);}void judge(){    char c=q2.top(); q2.pop();    if(c=='(') return ;    int a=q1.top();q1.pop();    int b=q1.top();q1.pop();    int ans=0;    if(c=='x')    {        int aa=0,bb=0;        while(a) aa+=a%10,a/=10;        while(b) bb+=b%10,b/=10;        ans=max(aa,bb);    }    else if(c=='*') ans=a*b;    else ans=a+b;    q1.push(ans);}int main(){    int t;    scanf("%d",&t);    while(t--)    {        while(!q1.empty()) q1.pop();        while(!q2.empty()) q2.pop();        scanf("%s",s);        int len=strlen(s);        for(int i=0; i<len; i++)        {            if(isdigit(s[i]))            {                get_num(i);                while(!q2.empty()&&q2.top()=='*')                judge();//先干掉乘法,只剩优先级低的加法            }            else            {                if(s[i]==',')                {                    while(!q2.empty()&&(q2.top()=='*'||q2.top()=='+'))                        judge();                }                else if(s[i]==')')                {                   while(!q2.empty()&&q2.top()!='(') judge();                   if(!q2.empty()&&q2.top()=='(') q2.pop();                   while(!q2.empty()&&(q2.top()=='*'||q2.top()=='x'))                       judge();                }                else                {                    if(s[i]=='S') i+=3;                    q2.push(s[i]);                }            }        }       while(!q2.empty()) judge();       printf("%d\n",q1.top());    }    return 0;}  //50//12*(2+3)//12+2*3//12*(2+3)+Smax(333,Smax(220,280))  /**************************************************************    Problem: 2271    User: 1506915059    Language: C++    Result: 正确    Time:0 ms    Memory:2684 kb****************************************************************/


表达式求值

时间限制:3000 ms  |  内存限制:65535 KB
难度:4
 这道题放在这里而不是最前面也是有原因的,很早之前做过几道简单的表达式求值的问题就自以为自己能写出这道题,结果写了一下午,而且有的问题还不能解决,在进一步了解过这种问题之后回来写简单多了,嗯,做事情一步一个脚印,慢慢来,理清思路写,不难。

 题意:四则运算表达式求结果,保留最后两位小数,题目保证没有除以0或者对0取余这种问题,而且保证运算过程中不会出现负数。

 这题没有符号开头,之前简单找过表达式求值的博客,了解过几个概念:中缀式、后缀式及前缀式。不过我只会用我自己的方法,怎么好写怎么写,怎么简洁怎么写,也许网上有成套的模板,但这并不是自己的代码风格,也许并不适合我,自己总结下来的才是最适合自己的。

 思路:还是按平常运算规则,先乘除再加减,出现括号就去括号,因为我们只要出现了乘除那么必定先运算完了,所以当出现')'的时候括号里面只有加减运算符了。最后符号栈里剩下的也就是些低级运算符了。

 你以为就我说的这么简单就可以了吗,步骤大家可能都知道,可是很多细节是特别需要注意的,要不然我也不会写了一个晚上加一个上午才能AC这道题。其实我们只需要注意这种情况:当前要入栈的符号的优先级如果没有符号栈栈顶元素的优先级高,那么先将栈里的元素取出来该出栈出栈该运算运算该入栈入栈。

const int N=1e5+10;char s[N];stack<double>q1;stack<char>q2;int get_opp(char c){    if(c=='*'||c=='/') return 3;    else if(c=='-'||c=='+') return 2;    else return 0;}void change(){    char c=q2.top();    q2.pop();    if(c=='(') return ;    double a=q1.top();    q1.pop();    double b=q1.top();    q1.pop();    double ans=0;    if(c=='*') ans=a*b;    else if(c=='/') ans=b/a;    else if(c=='+') ans=a+b;    else if(c=='-') ans=b-a;//注意运算顺序    q1.push(ans);}void get_num(int &i){    double zhengshu=0,xiaoshu=0,x=0.1;    while(isdigit(s[i]))    {        zhengshu=zhengshu*10+(s[i]-'0');        i++;    }    if(s[i]=='.')//含小数部分    {        i++;        while(isdigit(s[i]))        {            xiaoshu+=x*(s[i]-'0');            x*=0.1;            i++;        }    }    i--;    q1.push(zhengshu+xiaoshu);}int main(){    int t;    scanf("%d",&t);    while(t--)    {        scanf("%s",s);        while(!q1.empty()) q1.pop();        while(!q2.empty()) q2.pop();        for(int i=0; s[i]!='='; i++)        {            if(isdigit(s[i])) get_num(i);            else if(s[i]==')')//括号优先级最高            {                while(!q2.empty()&&q2.top()!='(') change();//注意边界处理                if(!q2.empty()&&q2.top()=='(') q2.pop();            }            else            {                int p1=get_opp(s[i]);                while(p1>0&&!q2.empty()&&p1<=get_opp(q2.top()))//优先级高的先运算                    change();                q2.push(s[i]);            }        }        while(!q2.empty()) change();        printf("%.2f\n",q1.top());    }    return 0;}



0 0