感觉以前从来没有用过,突然在白书上面看见。。。。
概要 中缀表达式 后缀表达式 表达式树
一.相关概念
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,一个栈存后缀表达式,另一个栈维护表达式运算符优先级严格单调上升;
| 扫描字符 | S | ans |
| a | NULL | a |
| + | + | 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 - * + |
| e | - | a b c d - * + e |
| / | - / | a b c d - * + e |
| f | - / | a b c d - * + e f |
| NULL | - / ---> NULL | a 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;