棧的一個應用是求四則運算表達式的值,這裡的表達式包含數字、加減乘除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;
}
}