二叉樹是表達式處理的經常使用工具,例如,a+b*(c-d)-e/f能夠表示成以下所示的二叉樹git
其中,每一個非葉子節點表示一個運算符,左子樹是第一個運算數對應的表達式,右子樹是第二個表達式對應的表達式。每一個葉子節點都是數。數組
其在空間利用上也很是高效,節點數等於表達式的長度。工具
lrj說方法有不少種,下面介紹他講的一種:找到「最後計算」的運算符(它是整個表達式樹的根),而後遞歸處理左右兩邊。ui
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 }
前綴表達式和後綴表達式分別對應表達式樹前序和後序遍歷的結果,若是不考慮括號,中綴表達式對應表達式樹中序遍歷的結果。spa
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 }
有了表達式樹求值就很是簡單了,先求左子樹的值,再求右子樹值,而後根據根節點的運算符計算最終的值。整個過程採用遞歸求解。code
(注意!!這種方法只能求個位數表達式的值)blog
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(")"); } }
最一個加一個認真寫的 胡亂寫的能實現嵌套括號、多位數相加減乘除的完整代碼string
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 }