四則運算 C++ 棧實現
阿新 • • 發佈:2019-02-07
#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);//字尾表示式儲存在pb棧中
printf("字尾表示式:");
ShowStack(pb);
return BackExpToResult(pb);
}
//字串轉換成中綴表示式
//棧ps最後儲存的就是原輸入的中綴表示式形式
void StringToMidExp(const char exp[], Stack *ps) {
//num陣列用來記錄數字的字元在相應位置上的值,以便後期還原成數值形式
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
{
//48是'0'的ASCII碼值
num[count] = exp[k] - 48; //num陣列記錄整數的每一位
count++; //count記錄整數的位數
}
//能夠進入這條分支的,就是運算子
else if ((exp[k] >= 40 && exp[k] <= 43) || exp[k] == 45 || exp[k] == 47) //運算子
{
//將該運算子之前的字元形式的數字轉換成與之對應的數值型
if (count > 0)
{
n = 0;
temp = 0;
//經過該while迴圈,字串形式的數字就被轉換成了數值型
while (n < count)
{
/**
這裡的TenPow是自己定義的一個函式TenPow(n)就是10的n次方
很明顯,在nun陣列中索引越小的數字位數越高
具體關係就是:
位數 = count-n-1 (n就是num陣列中的索引)
*/
temp += num[n] * TenPow(count - n -1); //每一位乘以10的某次方
n++;
}
//申請一個Node大小的記憶體空間,並用Node型指標q指向該地址
//給q的資料域和資料型別域賦值
q = (Node *)malloc(sizeof(Node));
q->type = NUM;
q->number = temp;
//將q壓進pm棧
Push(pm, q);
}
//為了下一波數字字串的錄入做準備,需要把count重置為0
count = 0; //位數清零
//運算子前面的數字進棧了,接下來就該運算子進棧了
q = (Node *)malloc(sizeof(Node));
//和上面數字進棧的方式相同,先賦值,然後壓棧
q->type = OP;
q->operation = exp[k];
Push(pm, q);
}
k++;
}
//既然輸入的是一個算數式,那麼末尾一定是一個數字
//由於沒有運算子了,while迴圈中的else if語句就不會觸發,最後一個數字就不會被轉換
//因此需要在迴圈結束之後處理最後一個數字
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);
}
//顛倒次序之後,在出棧的時候就是按照中綴表示式的從左至右的次序輸出的
//從始至終,pm都只是一個區域性變數,最後的中綴表示式是儲存在ps中的
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;
}
}
//中綴表示式轉換成字尾表示式
//有關中綴表示式轉換成字尾表示式的詳細內容,可以參考我的這篇部落格:
//http://blog.csdn.net/include_heqile/article/details/79036631
void MidExpToBackExp(Stack *pm, Stack *pb)
{
Stack tempStack, oprStack;
Stack *pt, *pr;
Node *q, *r;
pt = &tempStack; //臨時儲存字尾表示式
pr = &oprStack; //用來決定運算子的順序
InitStack(pt);
InitStack(pr);
//這個while迴圈就是中綴表示式轉換為字尾表示式的處理過程
//每一次迴圈,都會從中綴表示式的棧中讀取出一個元素
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//這個就是q->operation == ')' 的情況
{
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);//先把ps2清空,之後將ps1中的元素依次出棧,然後在壓到ps2中即可
while (ps1->top)
{
//臨時變數,用來暫時儲存出棧的元素
q = (Node *)malloc(sizeof(Node));
Pop(ps1, q);
Push(ps2, q);
}
}
else
{
ps2->top = NULL;
ps2->length = 0;
}
}