棧的典型應用-表達式求值【轉】


本文轉載自:http://www.voidcn.com/blog/lub0807/article/p-1510616.html

棧的一個應用是求四則運算表達式的值,這里的表達式包含數字、加減乘除4種運算符,以及小括號。

由於輸入是一個字符串,因此解決這個問題需要以下3個步驟:

1、輸入字符串轉化為中綴表達式;

2、中綴表達式轉化為后綴表達式;

3、后綴表達式求值。

現在表達式為:9 + ( 3 - 1 )* 3 + 10 / 2 ,先看一下運行結果:

首先解釋一下中綴表達式和后綴表達式的概念。所謂中綴表達式,就是我們平常書寫的表達式,因為運算符是寫在兩個參與運算的數字中間,所以叫中綴表達式,例如1 + 2 。與此對應,后綴表達式就是運算符寫在數字后面,比如剛才的算式就要寫成1 2 + ,我們看起來的確有點奇怪,不過計算機卻很喜歡這種表達式。下面分析解決問題的3個步驟。

1、輸入字符串轉化為中綴表達式

運算符號和括號好辦,本身就是字符,只需要把字符串形式的數字轉成相應的整數。我采用的方法是,用一個數組num[]存儲整數的各位數字,用一個整型變量count記錄位數。當下一個字符是運算符或者括號時,表示整數已經讀取完畢。這時候,把num數組的各位數字乘以10的某次方就可還原出該整數。該部分代碼如下:

//字符串轉換成中綴表達式
void StringToMidExp(const char exp[], Stack *ps) { int num[MAX_INT]; int k, n, count, temp; Stack m_stack; Stack *pm; Node *q; k = 0; count = 0; pm = &m_stack; InitStack(pm); while (exp[k] != '\0') { if (exp[k] >= '0' && exp[k] <= '9') //數字0到9 { count++; //count記錄整數的位數 num[count-1] = exp[k] - 48; //num數組記錄整數的每一位 } else if ((exp[k] >= 40 && exp[k] <= 43) || exp[k] == 45 || exp[k] == 47) //運算符 { if (count > 0) //轉換該運算符之前的數字 { n = 0; temp = 0; while (n < count) { temp += num[n] * TenPow(count - n -1); //每一位乘以10的某次方 n++; } q = (Node *)malloc(sizeof(Node)); q->type = NUM; q->number = temp; Push(pm, q); } count = 0; //位數清零 q = (Node *)malloc(sizeof(Node)); q->type = OP; q->operation = exp[k]; Push(pm, q); } k++; } if (count > 0) //把最后一個數字轉換出來 { n = 0; temp = 0; while (n < count) { temp += num[n] * TenPow(count - n -1); n++; } q = (Node *)malloc(sizeof(Node)); q->type = NUM; q->number = temp; Push(pm, q); } Reverse(pm, ps); //顛倒一下次序 } //計算10的n次方 int TenPow(int n) { if (n == 0) { return 1; } else { int i, k; i = 0; k = 1; while (i < n) { k *= 10; i++; } return k; } }

2、中綴表達式轉后綴表達式

 

這里需要一個棧用來暫時存儲運算符和括號,具體地,對中綴表達式從左到右按以下規則操作:

(1)遇到數字,則直接輸出;

(2)遇到左括號,則左括號進棧;

(3)遇到右括號,從棧頂開始依次輸出所有運算符,直到遇到左括號,這個左括號也出棧;

(4)遇到加號或減號,從棧頂開始依次輸出所有運算符,直到遇到左括號,但此時這個左括號不出棧,並且當前運算符進棧;

(5)遇到乘號或除號,如果棧頂是乘號或除號,則輸出,否則不輸出,並且當前運算符進棧。

這部分代碼如下:

//中綴表達式轉換成后綴表達式
void MidExpToBackExp(Stack *pm, Stack *pb) { Stack tempStack, oprStack; Stack *pt, *pr; Node *q, *r; pt = &tempStack; //臨時存儲后綴表達式 pr = &oprStack; //用來決定運算符的順序 InitStack(pt); InitStack(pr); while (pm->top) { q = (Node *)malloc(sizeof(Node)); Pop(pm, q); if (q->type == NUM) { Push(pt, q); } else { if (q->operation == '+' || q->operation == '-') { while (pr->top && pr->top->operation != '(') { r = (Node *)malloc(sizeof(Node)); Pop(pr, r); Push(pt, r); } Push(pr, q); } else if (q->operation == '*' || q->operation == '/') { while (pr->top && pr->top->operation != '(' && pr->top->operation != '+' && pr->top->operation != '-') { r = (Node *)malloc(sizeof(Node)); Pop(pr, r); Push(pt, r); } Push(pr, q); } else if (q->operation == '(') { Push(pr, q); } else { while (pr->top) { if (pr->top->operation == '(') { r = (Node *)malloc(sizeof(Node)); Pop(pr, r); free(r); break; } r = (Node *)malloc(sizeof(Node)); Pop(pr, r); Push(pt, r); } free(q); } } } while (pr->top) //棧內剩余運算符全部出棧 { r = (Node *)malloc(sizeof(Node)); Pop(pr, r); Push(pt, r); } Reverse(pt, pb); //顛倒一下次序 }

3、后綴表達式求值

 

這里還需要一個棧,只不過這回是用來暫時存儲數字,注意到后綴表達式中不含有括號了。具體地,對后綴表達式從左到右按以下規則操作:

(1)遇到數字,則數字進棧;

(2)遇到運算符,則棧頂數字出棧,記為num1,此時棧頂數字再出棧,記為num2,那么記num2 運算 num1 = num3,將num3進棧。還是舉個例子好了,比如遇到運算符+,棧頂數字是1,好了,1出棧。現在棧頂數字變為2了,好,2也出棧。現在計算2+1=3,此時,3進棧。

這部分代碼如下:

//根據后綴表達式計算結果
int BackExpToResult(Stack *ps) { if (!ps->top) //空棧說明表達式有誤 { return NO_RESULT; } Stack tempStack; Stack *pt; Node *q; int num_left, num_right, result; pt = &tempStack; InitStack(pt); while (ps->top) { if (ps->top->type == NUM) { q = (Node *)malloc(sizeof(Node)); Pop(ps, q); Push(pt, q); } else { q = (Node *)malloc(sizeof(Node)); Pop(pt, q); num_right = q->number; free(q); if (!pt->top) //pt棧內沒有第2個數了,說明表達式有誤 { return NO_RESULT; } q = (Node *)malloc(sizeof(Node)); Pop(pt, q); num_left = q->number; free(q); q = (Node *)malloc(sizeof(Node)); Pop(ps, q); switch(q->operation) { case '+': result = num_left + num_right; break; case '-': result = num_left - num_right; break; case '*': result = num_left * num_right; break; case '/': result = num_left / num_right; break; } free(q); q = (Node *)malloc(sizeof(Node)); q->type = NUM; q->number = result; Push(pt, q); } } q = (Node *)malloc(sizeof(Node)); Pop(pt, q); result = q->number; free(q); if (pt->top) //pt棧內還有數字,說明表達式有誤 { return NO_RESULT; } else { return result; } }

整個工作完成,不過,程序要想正確運行,對輸入有一些要求,比如:

 

(1)輸入不能有空字符;

(2)輸入的數字只能是整數,而且除數不能是0;

(3)確保中間的運算結果也都是整數,否則會舍棄小數部分,從而影響精度。

全部代碼如下:

#include <STDIO.H> #include <STDLIB.H> #define MAX_EXP 100 //表達式最大長度 #define MAX_INT 10 //整數最大位數 #define NO_RESULT -99999 //計算異常的返回值 enum node_type{ NUM, OP }; struct node{ int number; char operation; enum node_type type; struct node *next; }; struct stack{ struct node *top; int length; }; typedef struct node Node; typedef struct stack Stack; int GetResult(const char []); void StringToMidExp(const char [], Stack *); int TenPow(int); void MidExpToBackExp(Stack *, Stack *); int BackExpToResult(Stack *); void ShowStack(const Stack *); void ShowNode(const Node *); void InitStack(Stack *); void Push(Stack *, Node *); void Pop(Stack *, Node *); void ClearStack(Stack *); void Reverse(Stack *, Stack *); int main(void) { char expression[MAX_EXP]; int result; printf("輸入四則運算表達式:\n"); scanf("%s", expression); result = GetResult(expression); if (result == NO_RESULT) { printf("表達式有誤,計算失敗。\n"); } else { printf("計算結果是:%d\n", result); } return 0; } //根據表達式的字符串計算結果 int GetResult(const char exp[]) { Stack middleExp, backExp; Stack *pm, *pb; pm = &middleExp; pb = &backExp; InitStack(pm); InitStack(pb); StringToMidExp(exp, pm); printf("中綴表達式:"); ShowStack(pm); MidExpToBackExp(pm, pb); printf("后綴表達式:"); ShowStack(pb); return BackExpToResult(pb); } //字符串轉換成中綴表達式 void StringToMidExp(const char exp[], Stack *ps) { int num[MAX_INT]; int k, n, count, temp; Stack m_stack; Stack *pm; Node *q; k = 0; count = 0; pm = &m_stack; InitStack(pm); while (exp[k] != '\0') { if (exp[k] >= '0' && exp[k] <= '9') //數字0到9 { count++; //count記錄整數的位數 num[count-1] = exp[k] - 48; //num數組記錄整數的每一位 } else if ((exp[k] >= 40 && exp[k] <= 43) || exp[k] == 45 || exp[k] == 47) //運算符 { if (count > 0) //轉換該運算符之前的數字 { n = 0; temp = 0; while (n < count) { temp += num[n] * TenPow(count - n -1); //每一位乘以10的某次方 n++; } q = (Node *)malloc(sizeof(Node)); q->type = NUM; q->number = temp; Push(pm, q); } count = 0; //位數清零 q = (Node *)malloc(sizeof(Node)); q->type = OP; q->operation = exp[k]; Push(pm, q); } k++; } if (count > 0) //把最后一個數字轉換出來 { n = 0; temp = 0; while (n < count) { temp += num[n] * TenPow(count - n -1); n++; } q = (Node *)malloc(sizeof(Node)); q->type = NUM; q->number = temp; Push(pm, q); } Reverse(pm, ps); //顛倒一下次序 } //計算10的n次方 int TenPow(int n) { if (n == 0) { return 1; } else { int i, k; i = 0; k = 1; while (i < n) { k *= 10; i++; } return k; } } //中綴表達式轉換成后綴表達式 void MidExpToBackExp(Stack *pm, Stack *pb) { Stack tempStack, oprStack; Stack *pt, *pr; Node *q, *r; pt = &tempStack; //臨時存儲后綴表達式 pr = &oprStack; //用來決定運算符的順序 InitStack(pt); InitStack(pr); while (pm->top) { q = (Node *)malloc(sizeof(Node)); Pop(pm, q); if (q->type == NUM) { Push(pt, q); } else { if (q->operation == '+' || q->operation == '-') { while (pr->top && pr->top->operation != '(') { r = (Node *)malloc(sizeof(Node)); Pop(pr, r); Push(pt, r); } Push(pr, q); } else if (q->operation == '*' || q->operation == '/') { while (pr->top && pr->top->operation != '(' && pr->top->operation != '+' && pr->top->operation != '-') { r = (Node *)malloc(sizeof(Node)); Pop(pr, r); Push(pt, r); } Push(pr, q); } else if (q->operation == '(') { Push(pr, q); } else { while (pr->top) { if (pr->top->operation == '(') { r = (Node *)malloc(sizeof(Node)); Pop(pr, r); free(r); break; } r = (Node *)malloc(sizeof(Node)); Pop(pr, r); Push(pt, r); } free(q); } } } while (pr->top) //棧內剩余運算符全部出棧 { r = (Node *)malloc(sizeof(Node)); Pop(pr, r); Push(pt, r); } Reverse(pt, pb); //顛倒一下次序 } //根據后綴表達式計算結果 int BackExpToResult(Stack *ps) { if (!ps->top) //空棧說明表達式有誤 { return NO_RESULT; } Stack tempStack; Stack *pt; Node *q; int num_left, num_right, result; pt = &tempStack; InitStack(pt); while (ps->top) { if (ps->top->type == NUM) { q = (Node *)malloc(sizeof(Node)); Pop(ps, q); Push(pt, q); } else { q = (Node *)malloc(sizeof(Node)); Pop(pt, q); num_right = q->number; free(q); if (!pt->top) //pt棧內沒有第2個數了,說明表達式有誤 { return NO_RESULT; } q = (Node *)malloc(sizeof(Node)); Pop(pt, q); num_left = q->number; free(q); q = (Node *)malloc(sizeof(Node)); Pop(ps, q); switch(q->operation) { case '+': result = num_left + num_right; break; case '-': result = num_left - num_right; break; case '*': result = num_left * num_right; break; case '/': result = num_left / num_right; break; } free(q); q = (Node *)malloc(sizeof(Node)); q->type = NUM; q->number = result; Push(pt, q); } } q = (Node *)malloc(sizeof(Node)); Pop(pt, q); result = q->number; free(q); if (pt->top) //pt棧內還有數字,說明表達式有誤 { return NO_RESULT; } else { return result; } } //顯示棧中元素 void ShowStack(const Stack *ps) { if (ps->top) { Node *p = ps->top; while (p->next) { ShowNode(p); printf(" "); p = p->next; } ShowNode(p); printf("\n"); } else { printf("無\n"); } } //顯示一個節點元素 void ShowNode(const Node *p) { if (p->type == NUM) { printf("%d", p->number); } else { printf("%c", p->operation); } } //初始化棧 void InitStack(Stack *ps) { ps->length = 0; ps->top = NULL; } //節點入棧 void Push(Stack *ps, Node *pn) { pn->next = ps->top; ps->top = pn; ps->length++; } //節點出棧 void Pop(Stack *ps, Node *pn) { if (ps->top) { Node *q = ps->top; pn->next = NULL; pn->number = q->number; pn->operation = q->operation; pn->type = q->type; ps->top = q->next; free(q); ps->length--; } else { pn = NULL; } } //清空棧 void ClearStack(Stack *ps) { Node *q; while (ps->top) { q = ps->top; ps->top = q->next; free(q); ps->length--; } } //反轉棧中元素的次序 void Reverse(Stack *ps1, Stack *ps2) { if (ps1->top) { Node *q; ClearStack(ps2); while (ps1->top) { q = (Node *)malloc(sizeof(Node)); Pop(ps1, q); Push(ps2, q); } } else { ps2->top = NULL; ps2->length = 0; } }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM