中缀表达式——表达式树
还记得之前讲过的波兰表达式和逆波兰表达式么?我再最后提了一句可以将中缀表达式像以下的方法解析成一个树:

波兰和逆波兰表达式分别是对上面得树进行前序和后序遍历。我们这里的表达式树,就是这个树。我们现在需要学习如何将中缀表达式转化为上面这个树。
代码实现
事实上我们可以简单的总结几个小结论:
1.叶节点一定是数字
2.没有括号的情况下,相对运算优先级低的运算符放在树的上层位置
根据上面的两个结论,我们对建树就有一个粗略的概念了,用l和r分别表示两个端点[l,r),如果r-l=1,代表表达式由一个结点组成,如果表达式合法,那么剩下的一定是一个数字,于是构造成叶节点。
对于一长段的表达式,我们需要遍历一遍找到合适的运算符作为根节点。但现在的问题如何处理括号改变运算优先级的情况。
事实上我们再深入观察一下可以发现,如果一个表达式括号外有运算符,那么括号外的运算符的优先级一定可以视为低于括号内的运算符。也就是说作为根节点的运算符,一定要在括号之外。
如果不存在这样的结点呢?说明整个表达式都在括号之内,脱去外层的括号即可。代码如下:
const int maxn=1000;//nc表示当前树里面的结点总数,lch[i]和rch[i]表示编号为i的结点左右子节点的编号
int lch[maxn],rch[maxn],nc=0; char op[maxn];//op[i]表示编号为i的结点存储的字符
int build_tree(char* s,int l,int r){//l和r分别表示遍历表达式某段区间的左右端点:[x,y)
int i,c1=-1,c2=-1,p=0;//p用于记录当前表达式出现的括号数,左括号+1,右括号-1
//p=0代表在括号外,p不等于0代表在括号内
//当我们寻找根节点时,我们优先需要寻找的是括号外的运算符作为根节点
//c1表示括号外最后出现的"+-"运算符,c2表示括号外最后出现的"*/"运算符
//因为对于一个表达式,没有括号下两种运算符的优先级是不同的
//加减后运算所以应当放在较为上层的位置
if (r-l==1){nc++; lch[u]=rch[u]=0; op[u]=s[l]; return nc;}
//当区间内仅一个字符,那么它显然为一个叶节点,当前的结点个数作为它的编号
//由于是叶结点,叶节点没有左右子节点,lch和rch的值设置为0
for (int i=l;i<r;i++){//将表达式的字符遍历一编,寻找括号外的运算符
switch(s[i]){
case'(':p++; break;
case')':p--; break;
case'+':case'-':if(!p)c1=i; break;
case'*':case'/':if(!p)c2=i; break;
}
} if (c1<0) c1=c2;//找不到括号外的加减号,再用优先级比较高的乘除号
if (c1<0) return build_tree(s,x+1,y-1);//整个表达式被一对括号括起来了
//以c1作为根节点递归遍历左子树和右子树
nc++; lch[nc]=build_tree(s,l,c1); rch[nc]=build_tree(s,c1+1,r);
op[nc]=s[c1]; return nc;
}
事实上我怀疑会不会转化成前缀表达式,之后做起来思路上更加清晰。
本文介绍了如何将中缀表达式转化为表达式树,强调了叶节点为数字以及运算符优先级在树结构中的体现。通过分析括号内外运算符的优先级关系,提出了构建树结构的方法,并提供了相关代码实现。

4004

被折叠的 条评论
为什么被折叠?



