逆波蘭表達式
先說一下中綴表達式,平時我們使用的運算表達式就是中綴表達式,例如1+3*2,中綴表達式的特點就是:二元運算符總是置於與之相關的兩個運算對象之間
人讀起來比較好理解,但是計算機處理起來就很麻煩,運算順序往往因表達式的內容而定,不具規律性
后綴表達式,后綴表達式的特點就是:每一運算符都置於其運算對象之后,以上面的中綴表達式1+2*3為例子,轉為后綴表達式就是123*+
下面先分析怎么把中綴表達式轉換為后綴表達式,這里我們考慮六種操作符'+'、'-'、'*'、'/'、'('、')',完成中綴轉后綴我們需要兩個數組,都以棧的方式來操作,一個數組用來存放后綴表達式(char num[100]),
一個數組用來臨時存放操作數(char opera[100])(這里說臨時存放,是因為最后都要入棧到后綴表達式數組num中,這個數組就相當於一個中轉站)
1、從左往右掃描中綴表達式(這里我們以1*(2+3)為例)
2、如果是數字那么將其直接入棧到數組num中
3、如果是操作數,需要進一步判斷
(1)如果是左括號'('直接入棧到數組opera中
(2)如果是運算符('+'、'-'、'*'、'/'),先判斷數組opera的棧頂的操作數的優先級(如果是空棧那么直接入棧到數組opera),如果是左括號那么直接入棧到數組opera中,如果棧頂是運算符,且棧頂運算符的優先級大於該運算符
那么將棧頂的運算符出棧,並入棧到數組num中,重復步驟3,如果棧頂運算符優先級小於該運算符,那么直接將該運算符入棧到opera中
(3)如果是右括號')',那么說明在opera數組中一定有一個左括號與之對應(在你沒輸錯的情況下),那么將opera中的運算符依次出棧,並入棧到num中,直到遇到左括號'('(注意左括號不用入棧到num)
4、如果中綴表達式掃描完了,那么將opera中的操作數依次出棧,並入棧到num中就可以了,如果沒有沒有掃描完重復1-3步
上面就是中綴表達式轉后綴表達式的步驟了,下面用圖來直觀的了解一下這個過程
需要注意的是:opera中操作數,越靠近棧頂,優先級越高,下面附上實現代碼

1 void PexpretoSexpre(char *ss) 2 { 3 char num[100] = "0"; /* 存儲后綴表達式 */ 4 char opera[100] = "0"; /* 存儲運算符 */ 5 /* 6 num----j 7 opera----op 8 ss----i 9 */ 10 int i, j, op; 11 12 op = i = j = 0; 13 14 while (ss[i] != '\0') 15 { 16 if (isdigit(ss[i])) /* 如果是數字 */ 17 { 18 num[j] = ss[i]; /* 數字直接入后綴表達式棧 */ 19 j++; 20 i++; 21 } 22 else 23 { 24 switch (ss[i]) /* 如果是操作數 */ 25 { 26 case '+': 27 { 28 if (op == 0) /* 如果是空棧 */ 29 { 30 PushOperation(opera, ss, &op, &i); /* 入運算符棧 */ 31 break; 32 } 33 if (opera[op-1] == '+' || opera[op-1] == '-' || opera[op-1] == '*' || opera[op-1] == '/' || opera[op-1] == ')' || opera[op-1] == '(') 34 { 35 switch (opera[op-1]) 36 { 37 case '+': 38 { 39 PushOperation(opera, ss, &op, &i); 40 break; 41 } 42 case '-': 43 { 44 PushOperation(opera, ss, &op, &i); 45 break; 46 } 47 case '*': 48 { /* 加法優先級低於乘法 */ 49 num[j] = opera[op-1]; /* 將操作數出棧 */ 50 opera[op-1] = ss[i]; /* 將新的操作數壓入棧中 */ 51 j++; 52 i++; 53 break; 54 } 55 case '/': 56 { 57 num[j] = opera[op-1]; 58 opera[op-1] = ss[i]; 59 j++; 60 i++; 61 break; 62 } 63 case '(': 64 { 65 PushOperation(opera, ss, &op, &i); 66 break; 67 } 68 } 69 } 70 break; 71 } 72 case '-': 73 { 74 if (op == 0) 75 { 76 PushOperation(opera, ss, &op, &i); 77 break; 78 } 79 if (opera[op-1] == '+' || opera[op-1] == '-' || opera[op-1] == '*' || opera[op-1] == '/' || opera[op-1] == ')' || opera[op-1] == '(') 80 { 81 switch (opera[op-1]) 82 { 83 case '+': 84 { 85 PushOperation(opera, ss, &op, &i); 86 break; 87 } 88 case '-': 89 { 90 PushOperation(opera, ss, &op, &i); 91 break; 92 } 93 case '*': 94 { 95 num[j] = opera[op-1]; 96 opera[op-1] = ss[i]; 97 j++; 98 i++; 99 break; 100 } 101 case '/': 102 { 103 num[j] = opera[op-1]; 104 opera[op-1] = ss[i]; 105 j++; 106 i++; 107 break; 108 } 109 case '(': 110 { 111 PushOperation(opera, ss, &op, &i); 112 break; 113 } 114 } 115 } 116 break; 117 } 118 case '*': 119 { 120 if (op == 0) 121 { 122 PushOperation(opera, ss, &op, &i); 123 break; 124 } 125 if (opera[op-1] == '+' || opera[op-1] == '-' || opera[op-1] == '*' || opera[op-1] == '/' || opera[op-1] == ')' || opera[op-1] == '(') 126 { 127 switch (opera[op-1]) 128 { 129 case '+': 130 { 131 PushOperation(opera, ss, &op, &i); 132 break; 133 } 134 case '-': 135 { 136 PushOperation(opera, ss, &op, &i); 137 break; 138 } 139 case '*': 140 { 141 PushOperation(opera, ss, &op, &i); 142 break; 143 } 144 case '/': 145 { 146 PushOperation(opera, ss, &op, &i); 147 break; 148 } 149 case '(': 150 { 151 PushOperation(opera, ss, &op, &i); 152 break; 153 } 154 } 155 } 156 break; 157 } 158 case '/': 159 { 160 if (op == 0) 161 { 162 PushOperation(opera, ss, &op, &i); 163 break; 164 } 165 if (opera[op-1] == '+' || opera[op-1] == '-' || opera[op-1] == '*' || opera[op-1] == '/' || opera[op-1] == ')' || opera[op-1] == '(') 166 { 167 switch (opera[op-1]) 168 { 169 case '+': 170 { 171 PushOperation(opera, ss, &op, &i); 172 break; 173 } 174 case '-': 175 { 176 PushOperation(opera, ss, &op, &i); 177 break; 178 } 179 case '*': 180 { 181 PushOperation(opera, ss, &op, &i); 182 break; 183 } 184 case '/': 185 { 186 PushOperation(opera, ss, &op, &i); 187 break; 188 } 189 case '(': 190 { 191 PushOperation(opera, ss, &op, &i); 192 break; 193 } 194 } 195 } 196 break; 197 } 198 case '(': 199 { 200 PushOperation(opera, ss, &op, &i); 201 break; 202 } 203 case ')': /* 如果遇到右括號 */ 204 { 205 while (opera[op-1] != '(') 206 { 207 num[j] = opera[op-1]; /* 將運算符棧中的元素依次入棧到后綴表達式棧中,直到遇到左括號為止 */ 208 j++; 209 op--; 210 } 211 op--; 212 i++; 213 break; 214 } 215 default: 216 { 217 printf("傳入表達式不符合要求\n"); 218 exit(0); 219 } 220 221 } 222 } 223 } 224 while (op != 0) 225 { 226 num[j] = opera[op-1]; /* 將運算符棧中的元素依次入棧到后綴表達式棧中 */ 227 j++; 228 op--; 229 } 230 num[j] = '\0'; 231 i = 0; 232 while (num[i] != '\0') /* 將后綴表達式存儲到傳入的形參ss中 */ 233 { 234 ss[i] = num[i]; 235 i++; 236 } 237 ss[i] = '\0'; 238 } 239 240 /* Function: 入運算符棧*/ 241 void PushOperation(char *opera, char *ss, int *op, int *s) 242 { 243 opera[*op] = ss[*s]; 244 (*op)++; 245 (*s)++; 246 }
后綴表達式的計算
完成了中綴表達式轉后綴表達式,接下來就是后綴表達式的計算了,后綴表達式的計算比中綴轉后綴要稍微簡單一點,只需要對我們轉換好的后綴表達式從左往右依次掃描,並依次入棧就行了,
意思是只需要用一個數組(double num[100])就OK了
需要考慮的情況如下
1、如果是數字,那么直接入棧到num中
2、如果是運算符,將棧頂的兩個數字出棧(因為我們考慮的運算符加、減、乘、除都是雙目運算符,只需要兩個操作數),出棧后對兩個數字進行相應的運算,並將運算結果入棧
3、直到遇到'\0'
下面用幾張圖,來直觀了解下這個過程,以上面轉換好的后綴表達式"123+*"為例(這里用ss來存儲后綴表達式,num來存儲計算結果,注意不要與上面圖中num搞混淆了)
(注意:這里將計算結果5入棧后,棧頂從之前的[3]變成[2])
到這里后綴表達式的計算就結束了,下面附上實現代碼

1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define MAX 100 5 6 void JudgeFopen_s(errno_t err); /* 判斷文件打開是否成功 */ 7 void ReadFile(FILE *fp, char *ss); /* 讀取文件內容 */ 8 double TransformCtoD(char ch); /* 將char類型數組的每一個元素轉換為double */ 9 void CalculateAndPush(double *num, int *i, int *j, char mm); /* 計算結果並入棧 */ 10 11 int main() 12 { 13 FILE *fp; 14 errno_t err; 15 16 char ss[MAX]; /* 存儲逆波蘭表達式 */ 17 int i = 0; 18 int j = 0; 19 double num[MAX]; /* 棧 */ 20 21 err = fopen_s(&fp, "E:\\ww.txt", "r"); 22 23 JudgeFopen_s(err); /* 判斷文件打開是否成功 */ 24 ReadFile(fp, ss); /* 讀取文件內容,存儲到ss中*/ 25 26 while (ss[i] != '\0') 27 { 28 if (ss[i] >= '0' && ss[i] <= '9') /* 如果是數字 */ 29 { 30 /* 因為num是char類型的,需要轉換為double類型方便計算 */ 31 num[j] = TransformCtoD(ss[i]); /* 將數字存儲到棧中 */ 32 j++; 33 i++; 34 } 35 else if (ss[i] == '+' || ss[i] == '-' || ss[i] == '*' || ss[i] == '/') 36 { 37 CalculateAndPush(num, &i, &j, ss[i]); /* 計算結果並入棧 */ 38 } 39 else if (ss[i] == '\n') /* 如果是換行符,結束循環*/ 40 { 41 break; 42 } 43 } 44 45 printf("%lf", num[0]); 46 47 return 0; 48 } 49 50 /* Function: 計算結果並入棧 */ 51 void CalculateAndPush(double *num, int *i, int *j, char mm) 52 { 53 switch (mm) 54 { 55 case '+': 56 { 57 num[(*j)-2] = num[(*j)-1] + num[(*j)-2]; 58 (*j)--; 59 (*i)++; 60 break; 61 } 62 case '-': 63 { 64 num[(*j)-2] = num[(*j)-1] - num[(*j)-2]; 65 (*j)--; 66 (*i)++; 67 break; 68 } 69 case '*': 70 { 71 num[(*j)-2] = num[(*j)-1] * num[(*j)-2]; 72 (*j)--; 73 (*i)++; 74 break; 75 } 76 case '/': 77 { 78 num[(*j)-2] = num[(*j)-1] / num[(*j)-2]; 79 (*j)--; 80 (*i)++; 81 break; 82 } 83 default: 84 { 85 exit(0); 86 } 87 } 88 } 89 /* Function: 判斷文件打開是否成功 */ 90 void JudgeFopen_s(errno_t err) 91 { 92 if (err != 0) 93 { 94 printf("文件打開失敗\n"); 95 system("pause"); 96 exit(0); 97 } 98 } 99 100 /* Function: 讀取文件內容*/ 101 void ReadFile(FILE *fp, char *ss) 102 { 103 int i = 0; 104 105 while (!feof(fp)) 106 { 107 fscanf_s(fp, "%c", &ss[i]); 108 i++; 109 } 110 ss[i-1] = '\0'; 111 } 112 113 /* Function: 將char類型數組的每一個元素轉換為double */ 114 double TransformCtoD(char ch) 115 { 116 return (double)(ch - '0'); 117 }