所謂表達式的求值就是從鍵盤上輸入一個四則運算表達式按下Enter后在屏幕上輸出表達式的結果。表達式的求值在計算機的應用中非常廣泛,例如編譯器中對所寫的程序表達式的編譯等。它也是數據結構課程中棧這一章節中非常重要的一個算法,通過實現這個算法可以更好的掌握和理解棧的相關操作。
中綴表達式是指運算符在運算數的中間,計算中綴表達式時需要用到兩個棧:數字棧和運算符棧。在整個中綴表達式求值的過程中主要涉及到的模塊有:棧的相關操作、優先級表的確立、輸入的待計算字符串拆分為數字和運算符以及運算處理等。
一)、整體算法思路
1)、設立操作數棧和運算符棧,設表達式結束的標志是字符#,運算符棧底初始化為#,約定#運算符的優先級最小(這樣做的目的是在當兩個#相遇時就可以確定表達式掃描結束了)。
2)、若當前掃描到的是操作數則果斷將此數壓棧進操作數棧,如果當前是符號棧則將該操作符和棧頂操作符進行優先級比較如果低於棧頂優先級則將操作符棧頂元素彈出並彈出兩個操作數進行運算,運算完畢將結果壓入棧中。如果當前符號的優先級高於棧頂優先級則將此運算符入棧。
3)、循環操作2直到輸入的表達式運算結束(運算符棧底的#和輸入的表達式的#相遇)此時如若操作數棧中只剩一個數字則表示運算成功,此數就是表達式的結果,如果不止一個數則表示輸入的表達式有誤。
二)、優先級表
根據程序的整體思路可知只有當前掃描到的運算符優先級小於運算符棧頂的運算符時才能將棧頂運算符進行運算,這也就是說我們要在同一運算級上運算符的優先級做一些小小的修改(將同一運算級上的運算符看作棧中的優先級總大於此時掃描的運算符)才能滿足同一運算級上從左至右運算的規則。因此就可以得到下表(比較順序為當前掃描比較棧中):
當前掃描 棧中 |
+ |
– |
* |
/ |
( |
) |
# |
+ |
< |
< |
> |
> |
> |
< |
< |
– |
< |
< |
> |
> |
> |
< |
< |
* |
< |
< |
< |
< |
> |
< |
< |
/ |
< |
< |
< |
< |
> |
< |
< |
( |
> |
> |
> |
> |
> |
= |
< |
) |
|
|
|
|
|
|
|
# |
> |
> |
> |
> |
> |
誤 |
|
三)、輸入的字符串拆分成數字和運算符
另一個難點在於將輸入的字符串拆分成數字和運算符,可以將此過程放入運算過程中進行,因為拆分出來的數字和運算符需要判斷是入棧還是運算,如果將拆分出來的數字和運算符重新存起來在進行運算操作的話太麻煩和太耗費空間。
具體的拆分方法可以采用while循環的方式,定義一個double型變量用來存儲目前獲取到的數據,用一個char型變量ch每次從輸入緩存區中讀取一個字符,再進行判斷,如果是數字則將每次得到的字符轉換成int型后放入data的個位,如果是運算符則進行運算符的優先級比較,如果是小數點則判斷data中是否已經存在小數點,如果存在則報錯,如果不存在則加入權位,權位最開始為0.1以后在出現運算符之前每次都給權位乘以0.1,在將其加入到data中時乘以權位即可。
代碼實現:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXSIZE 100 //數字棧 typedef struct oprd{ double data[MAXSIZE]; int top; }OPRD; //運算符棧 typedef struct optr{ char data[MAXSIZE]; int top; }OPTR; //因為涉及到兩個棧的操作,所以將棧相關的操作用宏定義寫成函數, //這樣就具有了通用性 //初始化棧 #define InitStack(StackType, stack) \ { \ *stack = (StackType *)malloc(sizeof(StackType)); \ *stack->top = -1; \ } //判棧空 #define EmptyStack(stack) \ ( \ stack->top == -1 \ ) //判棧滿 #define FullStack(stack) \ ( \ stack->top == MAXSIZE - 1 \ ) //入棧 #define PushStack(stack, value) \ { \ if (!FullStack(stack)){ \ stack->top++; \ stack->data[stack->top] = value; \ } \ else{ \ printf("棧已滿,無法入棧\n"); \ exit(-1); \ } \ } //出棧 #define PopStack(stack, value) \ { \ if (!EmptyStack(stack)){ \ *value = stack->data[stack->top]; \ stack->top--; \ } \ else{ \ printf("棧已空,無法出棧\n"); \ exit(-1); \ } \ } //取棧頂元素 #define GetStackTop(stack, value) \ { \ if (!EmptyStack(stack)){ \ *value = stack->data[stack->top]; \ } \ else{ \ printf("棧為空,無法取棧頂元素\n"); \ } \ } //優先級表 char compare(char ch, char top) { switch(ch){ case '+': case '-': if (top == '+' || top == '-' || top == '*' || top == '/') return '<'; //掃描的小於棧頂 else return '>'; //掃描的大於棧頂 break; case '*': case '/': if (top == '*' || top == '/') return '<'; else return '>'; break; case '(': if(top == ')'){ printf("輸入有誤!\n"); exit(-1); } return '>'; break; case ')': if (top == '(') return '='; else if(top == '#'){ printf("輸入有誤!\n"); exit(-1); } else{ return '<'; } break; case '#': return '<'; } } //輸入表達式並計算結果 double CalculateExp(void) { double result, tempNum1, tempNum2; double data = 0, expn; char ch, topSign, point = 'n', num = 'n'; OPTR *sign; OPRD *number; InitStack(OPTR, &sign); InitStack(OPRD, &number); PushStack(sign, '#'); printf("請輸入表達式:"); ch = getchar(); GetStackTop(sign, &topSign); while(ch != '#' || topSign != '#'){ if ('0' <= ch && ch <= '9' || ch == '.'){ if (ch == '.' && point == 'y'){ printf("表達式輸入有誤!\n"); exit(-1); } else if (ch == '.' && point == 'n'){ point = 'y'; expn = 0.1; } else{ if (point == 'y'){ data = data + expn * (ch - '0'); expn *= 0.1; } else{ data = data * 10 + (ch - '0'); } num = 'y'; } ch = getchar(); } else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' || ch == ')' || ch == '#'){ if (num == 'y'){ PushStack(number, data); num = 'n'; point = 'n'; data = 0; } GetStackTop(sign, &topSign); switch(compare(ch, topSign)){ case '<': //掃描運算符優先級小於棧頂元素 PopStack(sign, &topSign); PopStack(number, &tempNum1); PopStack(number, &tempNum2); switch(topSign){ case '+': result = tempNum1 + tempNum2; break; case '-': result = tempNum1 - tempNum2; break; case '*': result = tempNum1 * tempNum2; break; case '/': result = tempNum2 / tempNum1; break; } PushStack(number, result); break; case '>': //掃描運算符優先級大於棧頂元素 PushStack(sign, ch); ch = getchar(); break; case '=': //掃描運算符為右括號,匹配到了左括號 PopStack(sign, &topSign); ch = getchar(); break; } } else if (ch == '\n'){ ch = '#'; } else{ printf("輸入的表達式有誤!\n"); exit(-1); } GetStackTop(sign, &topSign); } PopStack(number, &result); //將結果從棧中取出來 if (!EmptyStack(number)){ //如果取出后棧不為空則表示輸入的表達式不正確 printf("表達式有誤!\n"); exit(-1); } return result; } int main(void) { printf("%lf\n", CalculateExp()); return 0; }