表达式转表达式二叉树

表达式树

二叉树是表达式处理的常用工具,例如,a+b*(c-d)-e/f可以表示成如下所示的二叉树

其中,每个非叶子节点表示一个运算符,左子树是第一个运算数对应的表达式,右子树是第二个表达式对应的表达式。每个叶子节点都是数。

其在空间利用上也非常高效,节点数等于表达式的长度。

表达式转二叉树

lrj说方法有很多种,下面介绍他讲的一种:找到“最后计算”的运算符(它是整个表达式树的根),然后递归处理左右两边。

 1 const int maxn = 1000 + 10; 
 2 char str[maxn];
 3 int lch[maxn + 1], rch[maxn + 1]; char op[maxn + 1];     //每个结点的左右子结点编号和字符
 4 int nc = 0;         //结点数 
 5 int build_tree(char* s, int x, int y) 
 6 {  
 7     int i, c1=-1, c2=-1, p=0;  
 8     int u; 
 9     if(y-x == 1)   //仅一个字符,建立单独结点 
10     {        
11         u = ++nc;    
12         lch[u] = rch[u] = 0; 
13         op[u] = s[x];   
14         return u;
15      } 
16 
17      for (i = x; i < y; i++)   //寻找根节点的位置
18      {
19          switch (s[i]) 
20          {
21             case '(':  p++; break;     
22             case ')':  p--; break;      
23             case '+':
24             case '-':  if (!p) c1 = i; break;      
25             case '*': 
26             case '/':  if (!p) c2 = i; break;
27          }
28      }  
29     if (c1 < 0) c1 = c2;     //找不到括号外的加减号,就用乘除号  
30     if(c1 < 0) return build_tree(s, x+1, y-1);     //整个表达式被一对括号括起来  
31     u = ++nc;  
32     lch[u] = build_tree(s, x, c1); 
33     rch[u] = build_tree(s, c1+1, y);  
34     op[u] = s[c1];  
35     return u;
36 }

前缀式、中缀式、后缀式

前缀表达式和后缀表达式分别对应表达式树前序和后序遍历的结果,如果不考虑括号,中缀表达式对应表达式树中序遍历的结果。

 1 //中序遍历
 2 void InOrder(int root)
 3 {
 4     if (lch[root] > 0)  InOrder(lch[root]);
 5     printf("%c ", op[root]);
 6     if (rch[root] > 0)  InOrder(rch[root]);
 7 }
 8 
 9 //后序遍历
10 void PostOrder(int root)
11 {
12     if (lch[root] > 0)  PostOrder(lch[root]);
13     if (rch[root] > 0)  PostOrder(rch[root]);
14     printf("%c ", op[root]);
15 }

表达式求值

有了表达式树求值就非常简单了,先求左子树的值,再求右子树值,然后根据根节点的运算符计算最终的值。整个过程采用递归求解。

(注意!!这种方法只能求个位数表达式的值)

 1 //根据表达式二叉树求值
 2 int cal(int root)
 3 {
 4     int ans = 0;
 5     char ch = op[root];
 6     if (isdigit(ch))  return ch - '0';
 7     switch (ch)
 8     {
 9     case '+':  ans = cal(lch[root]) + cal(rch[root]); break;
10     case '-':  ans = cal(lch[root]) - cal(rch[root]); break;
11     case '*':  ans = cal(lch[root]) * cal(rch[root]); break;
12     case '/':  ans = cal(lch[root]) / cal(rch[root]); break;
13     }
14     return ans;
15 }

打印二叉树(括号表达法)

root(lch,rch)像这样的格式,其中lch,rch本身也是这样的格式(递归定义)。整个过程类似于先序遍历,先打印根节点,再打印左子树,再打印右子树。

注意格式,如果缺少左、右子树用空格代替(如root( ,rch)),如果都没有,则括号也省掉。

//打印表达式二叉树
void PrintTree(int root)
{
    int flag = 0;        //标记是否有左右子树
    printf("%c", op[root]);
    if (lch[root] > 0)  flag += 1;
    if (rch[root] > 0)  flag += 2;
    if (flag == 0)  return;
    if (flag == 1)        //只有左子树
    {
        printf("(");
        PrintTree(lch[root]);
        printf(", )");
    }
    if (flag == 2)        //只有右子树
    {
        printf("( ,");
        PrintTree(rch[root]);
        printf(")");
    }
    if (flag == 3)        //左右子树都有
    {
        printf("(");
        PrintTree(lch[root]);
        printf(",");
        PrintTree(rch[root]);
        printf(")");
    }
}

 

 

最一个加一个认真写的 胡乱写的能实现嵌套括号、多位数相加减乘除的完整代码

  1 #include<stdio.h>
  2 #include<string>
  3 
  4 const int maxn = 1000 + 10; 
  5 char expr[maxn];        //原表达式
  6 int str[maxn];            //转换后的表达式
  7 int lch[maxn + 1], rch[maxn + 1],op[maxn + 1];     //每个结点的左右子结点编号和字符
  8 bool is_alpha1[maxn], is_alpha2[maxn];        //是否为操作符或括号,前者对s数组,后者对op数组
  9 int nc = 0;         //结点数 
 10 int cnt = 0;        //转换后字符串的长度
 11 
 12 // 把表达式exp转化成参数形式,并存到str中
 13 void analyse(char* expr)
 14 {
 15     int len = strlen(expr);
 16     int i = 0;
 17     while(i < len)
 18     {
 19         if (!isdigit(expr[i]))
 20         {
 21             str[cnt] = expr[i++];
 22             is_alpha1[cnt++] = true;
 23         }
 24         else
 25         {
 26             int tmp = 0;
 27             while (isdigit(expr[i]))
 28             {
 29                 tmp = tmp * 10 + expr[i] - '0';
 30                 i++;
 31             }
 32             str[cnt] = tmp;
 33             is_alpha1[cnt++] = false;
 34         }
 35     }
 36 }
 37 
 38 //表达式转表达式树
 39 int build_tree(int* s, int x, int y) 
 40 {  
 41     int i, c1=-1, c2=-1, p=0;  
 42     int u; 
 43     if(y-x == 1)   //仅一个字符,建立单独结点 
 44     {        
 45         u = ++nc;    
 46         lch[u] = rch[u] = 0; 
 47         op[u] = s[x];   
 48         if (is_alpha1[x])  is_alpha2[u] = true;
 49         return u;
 50      } 
 51 
 52      for (i = x; i < y; i++)   //寻找根节点的位置
 53      {
 54          if(s[i] == '(' && is_alpha1[i])   p++;
 55          if(s[i] == ')' && is_alpha1[i])   p--;
 56          if((s[i] == '+' || s[i] == '-') && is_alpha1[i])   if (!p) c1 = i;
 57          if((s[i] == '*' || s[i] == '/') && is_alpha1[i])    if (!p) c2 = i;
 58      }  
 59     if (c1 < 0) c1 = c2;     //找不到括号外的加减号,就用乘除号  
 60     if(c1 < 0) return build_tree(s, x+1, y-1);     //整个表达式被一对括号括起来  
 61     u = ++nc;  
 62     lch[u] = build_tree(s, x, c1); 
 63     rch[u] = build_tree(s, c1+1, y);  
 64     op[u] = s[c1];  
 65     if (is_alpha1[c1])  is_alpha2[u] = true;
 66     return u;
 67 }
 68 
 69 //先序遍历
 70 void PreOrder(int root)
 71 {
 72     if(!is_alpha2[root])  printf("%d ", op[root]);
 73     else  printf("%c ", op[root]);
 74 
 75     if (lch[root] > 0)  PreOrder(lch[root]);
 76     if (rch[root] > 0)  PreOrder(rch[root]);
 77 }
 78 
 79 //中序遍历
 80 void InOrder(int root)
 81 {
 82     if (lch[root] > 0)  InOrder(lch[root]);
 83 
 84     if (!is_alpha2[root])  printf("%d ", op[root]);
 85     else  printf("%c ", op[root]);
 86 
 87     if (rch[root] > 0)  InOrder(rch[root]);
 88 }
 89 
 90 //后序遍历
 91 void PostOrder(int root)
 92 {
 93     if (lch[root] > 0)  PostOrder(lch[root]);
 94     if (rch[root] > 0)  PostOrder(rch[root]);
 95 
 96     if (!is_alpha2[root])  printf("%d ", op[root]);
 97     else  printf("%c ", op[root]);
 98 }
 99 
100 //根据表达式二叉树求值
101 int cal(int root)
102 {
103     int ans = 0;
104     int ch = op[root];
105     if (!is_alpha2[root])  return ch;
106     switch (ch)
107     {
108     case '+':  ans = cal(lch[root]) + cal(rch[root]); break;
109     case '-':  ans = cal(lch[root]) - cal(rch[root]); break;
110     case '*':  ans = cal(lch[root]) * cal(rch[root]); break;
111     case '/':  ans = cal(lch[root]) / cal(rch[root]); break;
112     }
113     return ans;
114 }
115 
116 //打印表达式二叉树
117 void PrintTree(int root)
118 {
119     int flag = 0;        //标记是否有左右子树
120     if (!is_alpha2[root])  printf("%d", op[root]);
121     else  printf("%c", op[root]);
122 
123     if (lch[root] > 0)  flag += 1;
124     if (rch[root] > 0)  flag += 2;
125     if (flag == 0)  return;
126     if (flag == 1)        //只有左子树
127     {
128         printf("(");
129         PrintTree(lch[root]);
130         printf(", )");
131     }
132     if (flag == 2)        //只有右子树
133     {
134         printf("( ,");
135         PrintTree(rch[root]);
136         printf(")");
137     }
138     if (flag == 3)        //左右子树都有
139     {
140         printf("(");
141         PrintTree(lch[root]);
142         printf(",");
143         PrintTree(rch[root]);
144         printf(")");
145     }
146 }
147 
148 int main()
149 {
150     printf("表达式:");
151     scanf("%s", expr);
152 
153     analyse(expr);   //转化
154 
155     
156     build_tree(str, 0, cnt);        //建树
157 
158     int root = 1;                            
159     printf("先序遍历:"); PreOrder(root); printf("\n");            //三种遍历
160     printf("中序遍历:"); InOrder(root); printf("\n");
161     printf("后序遍历:"); PostOrder(root); printf("\n");
162 
163     printf("表达式二叉树:"); PrintTree(root); printf("\n");        //打印表达式二叉树
164 
165     int ans;
166     ans = cal(root);                //表达式求值
167     printf("表达式值:%d\n", ans);
168 
169     return 0;
170 }

 

posted @ 2018-11-19 21:50  Rogn  阅读(15300)  评论(0编辑  收藏  举报