表达式转表达式二叉树


表达式树

二叉树是表达式处理的常用工具,例如,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 }

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM