使用棧實現表示式求值
阿新 • • 發佈:2018-10-31
看書學了一晚上這個內容,終於實現了
分為三個步驟:
0. 檢查輸入是否有誤(因為輸入其他的非預期字元,程式就會崩潰,我就試著加了一個檢查輸入的函式)
1. 先將正常的中綴表示式轉換為字尾表示式
2. 再進行求值
根據字尾表示式求值比較簡單,因為字尾表示式已經有了優先順序。
比較難懂的是將中綴表示式轉換為字尾表示式,需要考慮很多情況:
1. 如果字元是 '(' ,就直接入操作符棧,因為越內層的括號優先順序越高,所以不用考慮什麼
2. 如果字元是 ')' ,就要結束一個左括號了,將前面的操作符出棧送入postexp,直到遇到 '('。
3. 如果字元是 '-' 或 '+' ,就要將遇到的第一個 '(' 之前的所有操作符出棧送入postexp。因為相對來說,後面的 '+' 和 '-' 優先順序是最低的,低於前面的所有操作符。最後出棧完,再將它壓入棧。
4. 如果字元是 '*' 或 '/' , 先判斷前面操作符棧底是否為 '*' 或 '/' ,是的話就要將棧裡的符號出棧送入postexp。因為 '*' 或 '/' 的優先順序低於前面的 '*' 或 '/',是高於前面的 '+' 和 '-' 的。
#include <iostream> using namespace std; const int MAXSIZE = 50; typedef struct { char data[MAXSIZE]; int top; }Stack; void InitStack(Stack *&s) { s = (Stack*)malloc(sizeof(Stack)); s->top = -1; } bool Push(Stack *&s, char e) { if (s->top == MAXSIZE - 1) return false; s->top++; s->data[s->top] = e; return true; } bool Pop(Stack *&s, char &e) { if (s->top == -1) return false; e = s->data[s->top]; s->top--; return true; } bool GetTop(Stack *&s, char &e) { if (s->top == -1) return false; e = s->data[s->top]; return true; } bool StackEmpty(Stack *&s) { if (s->top == -1) return true; return false; } void trans(char *exp, char postexp[]) { int i = 0; // postexp 下標 char e; // 給 pop gettop用 Stack *Optr; InitStack(Optr); while (*exp != '\0') { switch (*exp) { case '(': Push(Optr, '('); exp++; //exp指標前移,繼續處理下一個字元 break; case ')': //右括號時,一直出棧,指導遇到 ( Pop(Optr, e); while (e != '(' ) // 當 e='(' 時,正好把它丟棄了 { postexp[i++] = e; Pop(Optr, e); } exp++; break; case '+': case '-': // + - 優先順序相同,當做同一種情況處理 while (!StackEmpty(Optr)) //操作符棧只要不為空,則一直掃描出棧,直到遇到 ) 。 { GetTop(Optr, e); if (e == '(') //後面的 + - ,優先順序最低,最後考慮;如果e是 (,則直接入棧即可,所以break break; else { postexp[i++] = e; Pop(Optr, e); } } Push(Optr, *exp); //最後將 + - 入棧 exp++; break; case '*': case '/': while (!StackEmpty(Optr)) { GetTop(Optr, e); if (e == '/' || e == '*') // * / 的優先順序僅僅低於它前面的 * /,高於前面的 + -,所以要將前面的 * / 彈出棧;+ - 保留,因為新的 * / 會放在棧低,優先順序高。 { postexp[i++] = e; Pop(Optr, e); } else break; // 其他情況( + - 左括號 )退出, } Push(Optr, *exp); //最後將 / * 入棧 exp++; break; default: while (*exp > '0' && *exp < '9') //迴圈判斷是否為數字字元,如果是則儲存到postexp,迴圈判斷是因為可能是多位數字 { postexp[i++] = *exp; exp++; } postexp[i++] = '#'; //以#標誌一個數字字串結束 } } while (!StackEmpty(Optr)) //掃描完exp後,操作符棧可能還有操作符,將其存到postexp { Pop(Optr, e); postexp[i++] = e; } postexp[i] = '\0'; //結束字串 free(Optr); //銷燬棧 } //--------- 下面是針對數字型棧的 typedef struct { double data[MAXSIZE]; int top; }Stack_num; void InitStack_num(Stack_num *&s) { s = (Stack_num *)malloc(sizeof(Stack_num)); s->top = -1; } bool Push_num(Stack_num *&s, double e) { if (s->top == MAXSIZE - 1) return false; s->top++; s->data[s->top] = e; return true; } bool Pop_num(Stack_num *&s, double &e) { if (s->top == -1) return false; e = s->data[s->top]; s->top--; return true; } //--------- double compvalue(char *postexp) { Stack_num *num; //運算元棧 InitStack_num(num); double result; //結果 double a, b; //彈出棧的兩個數 double c; //計算彈出棧的兩個數 double d; //將連續的數字字元轉換成整數儲存在d裡 while (*postexp != '\0') { switch (*postexp) { case '+': Pop_num(num, a); //因為字尾表示式已經有了優先順序了,所以可以直接彈出兩個數進行計算 Pop_num(num, b); c = a + b; Push_num(num, c); break; case '-': Pop_num(num, a); Pop_num(num, b); c = b - a; //注意是b-a,因為a先出來,是後面的數字 Push_num(num, c); break; case '*': Pop_num(num, a); Pop_num(num, b); c = a * b; Push_num(num, c); break; case '/': Pop_num(num, a); // a是除數 Pop_num(num, b); if (a != 0) { c = b / a; Push_num(num, c); } else { cout << "除0錯誤!" << endl; exit(0); } break; default: d = 0; while (*postexp >= '0' && *postexp <= '9') //當 *postexp = # 時,就忽略了。 { d = 10 * d + (*postexp - '0'); postexp++; } Push_num(num, d); } postexp++; //繼續下一個字元 } Pop_num(num, result); return result; } bool test(char *exp) { // start - 是否非法字元 for (int i = 0; exp[i] != '\0'; i++) { if ( !((exp[i] >= '0' && exp[i] <= '9') || exp[i] == '+' || exp[i] == '-' || \ exp[i] == '*' || exp[i] == '/' || exp[i] == '(' || exp[i] == ')') ) return false; } // end - 是否非法字元 // start - 括號是否匹配 Stack *s; char e; InitStack(s); for (int i = 0; exp[i] != '\0'; i++) { switch (exp[i]) { case '(': Push(s, exp[i]); break; case ')': if (Pop(s, e)) { if (exp[i] == ')' && e == '(') return true; else return false; } else return false; break; } } if (s->top != -1) //棧為空才認為成功 return false; else return true; // end - 括號匹配 return true; } int main() { char exp[MAXSIZE]; char postexp[MAXSIZE]; while (true) { cout << "輸入表示式:"; cin >> exp; if (!test(exp)) { cout << "非法字元 或 括號不匹配!" << endl; continue; } trans(exp, postexp); cout << "字尾表示式:" << postexp << endl; cout << "結果:" << compvalue(postexp) << endl; } system("pause"); return 0; }