表达式总结

感觉以前从来没有用过,突然在白书上面看见。。。。

概要   中缀表达式    后缀表达式    表达式树

一.相关概念

1.中缀表达式:

        就是熟知的表达式形式例如a+b*(c-d)-e/f,按照一定的优先级运算,不同的语言优先级不同,C语言应该是下表:

(注pascal的运算符and和*/统计,or,xor和+-同级)

2.后缀表达式:

    后缀表达式,指的是不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则);一般我们只考虑常用二目运算符和(),例如上边的a+b*(c-d)-e/f的后缀表达式为:abcd-*+ef/-

后缀表达式的计算:
    对人脑来说不如中缀表达式好看,但是对计算机来说很有用。计算时拿一个栈来存放结果,从左往右一次扫描后缀式如果为数字(a,b,c,d,e,f)就直接加入栈中,如果为运算符(-*/-)就取出栈顶的两个元素进行运算,注意这样的运算是有顺序的,即 次栈顶 (运算) 栈顶 ,因为类似减号这样的东西是没有交换律的;

中缀表达式转后缀表达式:

    用两个栈ans,S,一个栈存后缀表达式,另一个栈维护表达式运算符优先级严格单调上升;

扫描字符Sans
aNULLa
++a
b+a b
*+ *a b
(+ * (a b
c+ * ( a b c 
-+ * ( - a b c 
d+ * ( - a b c d
)+ * ( – ) -->  +  *  a b c d -
-+ *-   -->   -a b c d - * +
ea b c d - * + e
/- /a b c d - * + e 
f- / a b c d - * + e f
NULL-  /   --->  NULLa b c d - * + e f / -
   

 

我们从左到右扫描中缀表达式,遇到一个数字就直接加入ans,如果是运算符,先考虑运算符和S栈顶运算符的优先级,如果当前优先级小于或者等于栈顶的优先级,则弹出栈顶运算符并将其加入ans,最后再将当前的运算符加入s;特别的,当遇到’(‘时就直接加入S,遇到’)’是就将S中的最后一个’(’之后的元素依次弹出并加入ans;最后将S里面所有剩下的元素弹出并加入ans;

3.表达式树:

     表达式树就是一颗叶子结点是数,非叶子结点是运算符的二叉树(二目运算符),对于一个非叶子结点,它的左子树就代表了这个运算符左边的表达式,右子树为右边的表达式;

可以发现这样的树的左右儿子也是有顺序的,不能随意交换。

表达式树的计算:

    对左右子树递归,再将递归结果用当前节点的运算符计算即可;所以表达式树用树形结构来替代了表达式的结构;

和中缀,后缀表达式的关系:

    中缀表达式是表达式树的中序遍历再加上某些必要的括号,后缀表达式就是表达式树的后序遍历;

    后缀表达式转表达式树,理解了表达式树的计算之后,我们只需要按照后缀表达式求值的方式建树,依旧用一个栈存字符代表的编号,每次运算符进入时退出栈顶两个元素,并接到当前运算符的左右儿子再把当前运算符标号加进去;

    中缀表达式转表达式树:

    一种出现在白书上比较简单的方法是:找整个中缀里面最后一个运算的运算符,左儿子就递归其左边的表达式,右儿子就递归其右边的表达式,但是我感觉这样子的复杂度是O(n*dep)dep为表达式树的深度。

    O(n)的我能想到的方法就是参照中缀转后缀的思想在运算符进入ans数组时直接把左右儿子接到当前运算符并弹出再加入(估计没什么用,不会卡);

二.一些应用

   1. UVA – 12219   Common Subexpression Elimination

   题意:给出一个二目表达式,中缀的形式,现在可以允许用一个数字替换第二次出现的相同表达式,求最少可以用多少个字符表示出这个表达式;

   题解:建立表达式树会直观一点,建好树之后,问题即你可以用一个数字替代前面出现的相同子树,并且会保留第一次出现的子树,这样只要贪心就好,用三元(u,l,r)表示当前节点,左子树,右子树;将每个子树都哈希一下,比较当前节点和左子树和右子树是否相同,用一个map标记是否出现;

   (直接hash一个子树的话应该也是没问题)

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<map>
 5 using namespace std;
 6 const int N=500010; 
 7 int T,vis[N],idx;
 8 char s[N],*p;
 9 struct node{
10     string s;
11     int l,r,h;
12     bool operator <(const node&A)const{
13         if(A.h!=h)return h<A.h;
14         if(A.l!=l)return l<A.l;
15         return r<A.r;
16     }
17 }v[N];
18 map<node,int>mp;
19 int solve(){
20     int now=++idx;
21     vis[now]=0;
22     node &u=v[now];
23     u.s="";u.l=u.r=u.h=0;
24     while(isalpha(*p)){
25     u.h=u.h*27+*p-'a'+1;//
26     u.s=u.s+*p;
27     p++;
28     }
29     if(*p=='('){
30         p++;u.l=solve();
31         p++;u.r=solve();
32         p++;
33     } //
34     if(mp[u]){
35         idx--;
36         return mp[u];
37     }
38     else return mp[u]=now;
39 }///
40 void print(int x){
41     if(vis[x]){printf("%d",x);return;}
42     vis[x]=1;
43     printf("%s",v[x].s.c_str());
44     if(v[x].l&&v[x].r){
45         printf("(");
46         print(v[x].l);
47         printf(",");
48         print(v[x].r);
49         printf(")");
50     }
51 }//
52 int main()
53 {    freopen("A.in","r",stdin);    
54     freopen("A.out","w",stdout);
55     scanf("%d",&T);
56     while(T--){
57         mp.clear(); 
58         scanf("%s",s+1);
59         p=s+1;idx=0;
60         int tmp=solve();
61         print(tmp);
62         printf("\n");
63     }
64     return 0;
65 }//by tkys_Austin; 
View Code

 

 

 

   2.UVA – 1661  Equation 

  题意:输入一个最多有一个x,x最多出现一次的方程,求解,如果有且仅有一个解,输出分数形式;

  题解:首先定义一个分数类用来存储答案,后缀表达式转表达式树,对于一个节点,将没有x的子树的值直接计算,再根据当前节点的结果和这个已知子树算出未知子树的值,递归求解直到叶子x;

         但是代码写起来好像很不友好,特别是无解和多解的讨论:
         左子树已知: a/x=b;  a!=0,b!=0一解; a!=0,b=0,无解;  a=0,b=0多解;   a=0,b!=0无解;

                        a*x=b;  a!=0,一解;     a=0,b=0,多解;   a=0,b!=0无解;        

        右子树已知:  x*a=b;  a!=0,一解;     a=0,b=0,多解;   a=0,b!=0无解;

                        x/a=b(a!=0);             一解;

       WA了好多发的。。。。( 初中数学。。。。。。。。。)

   

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cmath>
  4 #include<cstring>
  5 #define Run(i,l,r) for(ll i=l;i<=r;i++)
  6 #define Don(i,l,r) for(ll i=l;i>=r;i--)
  7 using namespace std;
  8 typedef long long ll;
  9 const int N=1100000;
 10 char ch; 
 11 ll cnt,ls[N],rs[N],vis[N],top,id[N],fg1,fg2,st[N];
 12 ll gcd(ll a,ll b){return(!b)?a:gcd(b,a%b);}//
 13 struct Fra{
 14     ll a,b;
 15     Fra(ll A=0,ll B=1):a(A),b(B){};//
 16     void sim(){
 17         if(b<0)a=-a,b=-b;
 18         if(a==0){b=1;return;}
 19         ll g=gcd(abs(a),b);
 20         if(g==1||g==0)return;
 21         a/=g;b/=g;
 22         return;
 23     }//
 24     Fra operator +(const Fra&A)const{
 25         Fra ret=Fra(a*A.b+A.a*b,b*A.b);
 26         ret.sim();
 27         return ret;
 28     }//
 29     Fra operator -(const Fra&A)const{
 30         Fra ret=Fra(a*A.b-A.a*b,b*A.b);
 31         ret.sim();
 32         return ret;
 33     }//
 34     Fra operator *(const Fra&A)const{
 35         Fra ret=Fra(a*A.a,b*A.b);
 36         ret.sim();
 37         return ret;
 38     }//
 39     Fra operator /(const Fra&A)const{
 40         Fra ret=Fra(a*A.b,b*A.a);
 41         ret.sim();
 42         return ret;
 43     }//
 44 }A[N],ans;
 45 Fra cal(Fra a,Fra b,ll op){
 46     if(op==0)return a+b;
 47     if(op==1)return a-b;
 48     if(op==2)return a*b;
 49     return a/b;
 50 }//
 51 Fra get(ll u){
 52     if(!ls[u]&&!rs[u])return A[u];
 53     return cal(get(ls[u]),get(rs[u]),A[u].a);
 54 }//
 55 void dfs(ll u,Fra now){
 56     if(!ls[u]&&!rs[u]){
 57         if(!vis[u]){
 58             Fra tmp=now-A[u];
 59             if(!tmp.a)fg2=1;else fg1=1;//
 60         }else ans=now;//
 61         return;//
 62     }// 
 63     if(vis[rs[u]]){
 64         Fra tmp=get(ls[u]);
 65         if(A[u].a>=2&&!tmp.a){
 66             if(!now.a)fg2=1;else fg1=1;//0*x=0,1 ; 0/x=0,1; 
 67             return;
 68         }//
 69         if(A[u].a==3&&!now.a){fg1=1;return;}//1/x=0;
 70         if(A[u].a==1)now.a=-now.a;
 71         if(A[u].a==3)swap(now.a,now.b);
 72         now=cal(now,tmp,A[u].a^1);//1*x=1,0 ; 1/x=1 ;
 73         dfs(rs[u],now);
 74     }else {
 75         Fra tmp=get(rs[u]);
 76         if(A[u].a>=2&&!tmp.a){
 77             if(!now.a)fg2=1;else fg1=1;   //x*0=0,1 ; x/0=0,1;
 78             return;
 79         }
 80         now=cal(now,tmp,A[u].a^1);       //x*1=0,1;  x/1=0,1;
 81         dfs(ls[u],now);
 82     }
 83 }
 84 void solve(){
 85     top=0;
 86     for(ll i=1;i<=cnt;i++){
 87         if(A[i].b || (!A[i].b&&!(~A[i].a)) )st[++top]=i;
 88         else {
 89             rs[i]=st[top--];
 90             ls[i]=st[top--];
 91             st[++top]=i;
 92             vis[i]=vis[ls[i]]||vis[rs[i]];
 93         }
 94     }//
 95     fg1=fg2=0;
 96     dfs(st[top],Fra(0,1));//
 97     if(fg1)puts("NONE");
 98     else if(fg2)puts("MULTIPLE");
 99     else printf("X = %lld/%lld\n",ans.a,ans.b);//
100 }//
101 int main()
102 {    freopen("E.in","r",stdin);
103     freopen("E.out","w",stdout);
104     while((ch=getchar())!=-1){
105         cnt=0;
106         memset(ls,0,sizeof(ls));
107         memset(rs,0,sizeof(rs));
108         memset(vis,0,sizeof(vis));
109         while(1){
110             if(ch=='X')A[++cnt]=Fra(-1,0),vis[cnt]=1,ch=getchar();
111             else if(ch=='+')A[++cnt]=Fra(0,0),ch=getchar();
112             else if(ch=='-')A[++cnt]=Fra(1,0),ch=getchar();
113             else if(ch=='*')A[++cnt]=Fra(2,0),ch=getchar();
114             else if(ch=='/')A[++cnt]=Fra(3,0),ch=getchar();
115             else A[++cnt]=Fra(ch-'0',1),ch=getchar();
116             while(ch==' ')ch=getchar(); 
117             if(ch=='\n'||ch==-1)break;
118         }
119         solve();
120     }
121     return 0; 
122 }//by tkys_Austin;
View Code

 


  3.UVA – 1662   Brackets Removal

   题意:给出一个中缀表达式,去掉不必要的括号再输出;

   题解:应该有其它的写法,我的写法是转表达式树,再中序递归地加上必要地括号,如果不加括号的话需要变号(括号前面是-或/),分类讨论:
         左右儿子的运算符优先级比当前节点运算符大,直接不加括号,比当前节点小,直接加括号;

         否则同级的运算符

         对于左子树,如果当前节点被改过,那么它也应该改;

         对于右子树,如果当前节点是-或/就改,实现时标记每个点是否被改就可以了;

        

    

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #define Run(i,l,r) for(int i=l;i<=r;i++)
 5 #define Don(i,l,r) for(int i=l;i>=r;i--)
 6 using namespace std;
 7 const int N=1010;
 8 int n,ls[N],rs[N],rt,sz;
 9 char op[N],s[N];
10 void build(int&k,int l,int r){
11     if(l==r){
12         k=++sz;ls[k]=rs[k]=0;op[k]=s[l];
13         return;
14     } //
15     int p1=0,p2=0,cnt=0;
16     for(int i=l;i<=r;i++){
17         switch(s[i]){
18             case'(':cnt++;break;
19             case')':cnt--;break;
20             case'+':case'-':if(!cnt)p1=i;break;
21             case'*':case'/':if(!cnt)p2=i;break;
22         }
23     }//
24     if(!p1)p1=p2;
25     if(!p1){build(k,l+1,r-1);return;}
26     op[k=++sz]=s[p1];
27     build(ls[k],l,p1-1);
28     build(rs[k],p1+1,r);
29 }///
30 int cmp(char C){
31     if(C=='+'||C=='-')return 0;
32     if(C=='*'||C=='/')return 1;
33     return 2;
34 }//
35 void change(int u){
36     char &tmp=op[u];
37     switch(tmp){
38         case '+':tmp='-';break;
39         case '-':tmp='+';break;
40         case '*':tmp='/';break;
41         case '/':tmp='*';break;
42     } 
43 }
44 string dfs(int u,int fg){
45     if(!ls[u]&&!rs[u]){return string("")+op[u];}
46     string ret="",le="",ri="";
47     int k=cmp(op[u]),l=cmp(op[ls[u]]),r=cmp(op[rs[u]]);
48     if(l>k)le=dfs(ls[u],0);
49     else if (l<k)le='('+dfs(ls[u],0)+')';
50     else {
51         if(fg)change(ls[u]);else fg=0;
52         le=dfs(ls[u],fg); 
53     }
54     if(r>k)ri=dfs(rs[u],0);
55     else if(r<k)ri='('+dfs(rs[u],0)+')';
56     else {
57         if(op[u]=='-'||op[u]=='/'){
58             fg=1;change(rs[u]);
59         }else fg=0;
60         ri=dfs(rs[u],fg);
61     }
62     return le+op[u]+ri;
63 }//
64 int main()
65 {    freopen("F.in","r",stdin);
66     freopen("F.out","w",stdout);
67     while(scanf("%s",s+1)!=EOF){
68         rt=0;sz=0;
69         n=strlen(s+1);
70         build(rt,1,n);
71         printf("%s\n",dfs(rt,0).c_str());//
72     }//
73     return 0;
74 }//by tkys_Austin;
View Code

 

 

 

 

 

 

 

 



 

 

 

 

大米飘香的总结:
       抓住三种形式之间的关系,灵活选取后缀或树解题,这类题实现的时候注意细节,一般和实际问题联系比较紧密,分类要全面,不然会很惨的。。。。。。。。。。。。。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

   

转载于:https://www.cnblogs.com/Paul-Guderian/p/9687432.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值