对于中缀表达式(e1)OP(e2),令根结点值为OP,左子树为e1,右子树e2,e1与e2递归。
如表达式:1+2*(3-4)-5/6 即 ((1) + ((2)*(3-4)))-((5)/(6))
可以形成如下的表达式树。
先序:-+1*2-34/56 前缀表达式
中序:1+2*3-4-5/6 中缀表达式
后序:1234-*+56/- 后缀表达式
其中后缀表达式可直接求值(不用考虑优先级)。但求后缀式过程中需用优先级,算符优先法实际在执行过程中隐含形成的逻辑结构就是一棵树。
那么给我们一个表达式,我们怎样将其转换为一个表达式树呢?
输入:一个表达式
输出:各个结点的编号:左孩子编号、右孩子编号、该结点表示的字符。
首先是存储结构定义:
//表达式树
int lch[maxn], rch[maxn], nc; //每个结点的左右子结点编号 结点数
char op[maxn]; //结点的字符
算法思想:
找到最后计算的运算符,是整颗表达式树的根,然后递归。当p为0的时候考虑这个运算符,因为括号里的运算符忽略,c1c2表示最优出现的加减号与乘除号, 如果括号外有加减号 ,肯定最后计算,如果没有加减号,考虑乘除。如果全部没有,说明整个表达式外面被一对括号括起来。去掉后递归调用最后运算的运算符s[c1] 它的左子树是区间[x,c1] 右子树是[c1+1,y]。
算法实现:
int build_tree(char *s, int x, int y)
{
/*找到最后计算的运算符,是整棵表达式树的根,然后递归
当p为0的时候考虑这个运算符,因为括号里的运算符忽略。c1c2表示最优出现
的加减号与乘除号 如果括号外有加减号,肯定最后计算,如果没有加减号,
考虑乘除如果全部没有,说明整个表达式外面被一对括号括起来,去掉后递归
调用最后运算的运算符s[c1] 它的左子树是区间[x,c1] 右子树是[c1+1,y] */
int c1, c2, p, u, i;
c1 = -1;
c2 = -1;
p = 0;
if(y - x == 1) //仅一个字符 建立单独结点
{
u = ++nc;
lch[u] = 0;
rch[u] = 0;
op[u] = s[x];
return u;
}
for(i = x; i < y; 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); //整个表达式被一对括号括起来
u = ++nc;
lch[u] = build_tree(s, x, c1);
rch[u] = build_tree(s, c1+1, y);
op[u] = s[c1];
return u;
}