鏈式表示的棧——鏈式棧4——算術表示式
通過鍵盤輸入一個表示式,如21-(15+7*9)/3,要求將其轉換為字尾表示式,並計算表示式的值。
【分析】求表示式的值是高階程式設計語言中編譯器設計的一個基本問題。它的實現藉助於棧的“後進先出”特性。
一個算術表示式是由運算元(運算物件)、運算子和分解符(括號)組成的有意義的式子。運算子從運算物件的個數上分為單目運算子和雙目運算子;從運算型別上可以分為算術運算子、關係運算符、邏輯運算子等,在此為了簡化問題考慮,我們假設算術運算子只包含加,減,乘,除雙目運算子和左右兩種圓括號。例如:
a-(b+c*d)/e
這種算術表示式中的運算子總出現在兩個運算元之間,這種算術表示式稱為中綴表示式。計算機編譯系統在計算一個算術表示式之前要將中綴表示式轉換為字尾表示式,然後對字尾表示式進行計算。字尾表示式就是算術運算子出現在運算元之後,並且不含括號。在計算機中求解算術表示式的值分為兩個步驟:
(1)將中綴表示式轉換為字尾表示式
(2)依據字尾表示式計算表示式的值
1.將中綴表示式轉換為字尾表示式
要將一箇中綴表示式轉換為字尾表示式,首先要了解算術四則運算的規則。算術的四則運算的規則是:
(1)算術表示式的優先順序:先乘除,後加減;
(2)有括號的先算括號裡的,後算括號外的,多層括號的由內到外進行;
(3)同級別的從左到右進行計算;
上面的算術表示式轉換為字尾表示式為:
a b c d * + e / -
不難看出字尾表示式具有以下3個特點:
(1)字尾表示式與中綴表示式的操作數出現順序相同,只是運算子先後順序改變了;
(2)字尾表示式不出現括號;
(3)字尾表示式的操作符出現在運算元之後。
字尾表示式也叫作逆波蘭表示式,是波蘭邏輯學家Jan.Lukasiewicz於1929年提出的表示方法。每個運算子都位於運算元之後,故稱為字尾表示。字尾表示式既無括號也無優先順序的約束,因此只需要從左到右依次掃描字尾表示式的各個字元,遇到運算子時,直接對運算子前面兩個運算元進行運算即可。
如何將中綴表示式轉換為字尾表示式呢?可以設定一個棧,用於存放運算子。依次讀入表示式中的每個字元,如果是運算元直接輸出。如果是運算子,則比較棧頂元素符與當前運算子的優先順序,然後進行處理,直到整個表示式處理完畢。我們約定‘#’作為字尾表示式的結束標誌,假設θ1為棧運算子,θ2為當前掃描的運算子。則中綴表示式轉換為字尾表示式的演算法描述如下:
(1)初始化棧,並將‘#’入棧;
(2)若當前字元是運算元,則將該運算元輸出,並讀入下一字元;
(3)若當前字元是運算子,記作θ2,將θ2與棧頂的運算子θ1比較。若θ1優先順序低於θ2,則將θ2進棧;若θ1優先順序高於θ2,則將θ1出棧並將其作為字尾表示式輸出。然後繼續比較新的棧頂運算子θ1和與當前運算子θ2的優先順序,若θ1的優先順序與θ2相等,且θ1為‘(’,θ2為‘)’,則將θ1出棧,繼續讀入下一個字元;
(4)如果θ2的優先順序與θ1相等,且θ1和θ2都為‘#’,將θ1出棧,棧為空。則完成中綴表示式轉換為字尾表示式,演算法結束。
運算子的優先順序關係如表所示:
利用上述演算法,將中綴表示式a-(b+c*d)/e轉換為字尾表示式的輸出過程如下表(為了便與描述,在表示式末尾加一個結束標誌‘#’)
2.字尾表示式的計算
在計算字尾表示式時,需要設定兩個棧:operator棧和openand棧。其中operator棧用於存放運算子,operand用於存放運算元和中間運算結果。具體運算思想如下:依次讀入字尾表示式中的每個字元,如果是運算元,則將運算元進入operand棧。如果是運算子,則將操作數出棧兩次,然後對運算元進行當前操作符的運算。直到整個表示式處理完畢。
LinkStack.h
#pragma once
#include <iostream>
#include <malloc.h>
using namespace std;
typedef char DataType;
typedef struct node
{
DataType data;
struct node *next;
}LStackNode,*LinkStack;
void InitStack(LinkStack *top)
{
if ((*top=(LinkStack)malloc(sizeof(LStackNode)))==NULL)
{
exit(-1);
}
(*top)->next = NULL;
}
int StackEmpty(LinkStack top)
{
if (top->next==NULL)
{
return 1;
}
else
{
return 0;
}
}
int PushStack(LinkStack top, DataType e)
{
LStackNode *p;
if ((p=(LStackNode*)malloc(sizeof(LStackNode)))==NULL)
{
cout << "記憶體分配失敗!";
exit(-1);
}
p->data = e;
p->next = top->next;
top->next = p;
return 1;
}
int PopStack(LinkStack top, DataType *e)
{
LStackNode *p;
p = top->next;
if (!p)
{
cout << "棧已空!";
return 0;
}
top->next = p->next;
*e = p->data;
free(p);
return 1;
}
int GetTop(LinkStack top, DataType *e)
{
LStackNode *p;
p = top->next;
if (!p)
{
cout << "棧已空!";
return 0;
}
*e = p->data;
return 1;
}
int StackLength(LinkStack top)
{
LStackNode *p;
int count = 0;
p = top;
while (p->next!=NULL)
{
p = p->next;
count++;
}
return count;
}
void DestoryStack(LinkStack top)
{
LStackNode *p, *q;
p = top;
while (!p)
{
q = p;
p = p->next;
free(q);
}
}
main.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include "LinkStack.h"
#define MAXSIZE 50
typedef struct
{
float data[MAXSIZE];
int top;
}OpStack;
void TranslateExpress(char s1[],char s2[]);
float ComputeExpress(char s[]);
void main()
{
char a[MAXSIZE], b[MAXSIZE];
float f;
cout << "請輸入一個算術表示式:" << endl;
gets_s(a);
cout << "中綴表示式為:" << a;
TranslateExpress(a,b);
cout << "字尾表示式為:" << b << endl;
f = ComputeExpress(b);
cout << "計算結果:" << f << endl;
system("pause");
}
float ComputeExpress(char a[])
{
OpStack S;
int i = 0, value;
float x1, x2;
float result;
S.top = -1;
while (a[i]!='\0')
{
if (a[i]!=' '&&a[i]>='0'&&a[i]<='9')
{
value = 0;
while (a[i]!=' ')
{
value = 10 * value + a[i] - '0';
i++;
}
S.top++;
S.data[S.top] = value;
}
else
{
switch (a[i])
{
case '+':
x1 = S.data[S.top];
S.top--;
x2 = S.data[S.top];
S.top--;
result = x1 + x2;
S.top++;
S.data[S.top] = result;
break;
case '-':
x1 = S.data[S.top];
S.top--;
x2 = S.data[S.top];
S.top--;
result = x2 - x1;
S.top++;
S.data[S.top] = result;
break;
case '*':
x1 = S.data[S.top];
S.top--;
x2 = S.data[S.top];
S.top--;
result = x1*x2;
S.top++;
S.data[S.top] = result;
break;
case '/':
x1 = S.data[S.top];
S.top--;
x2 = S.data[S.top];
S.top--;
result = x2 / x1;
S.top++;
S.data[S.top] = result;
break;
//default:
// break;
}
i++;
}
}
if (!S.top!=-1)
{
result = S.data[S.top];
S.top--;
if (S.top==-1)
{
return result;
}
else
{
cout << "表示式錯誤!";
exit(-1);
}
}
}
void TranslateExpress(char str[], char exp[])
{
LinkStack S;
char ch;
DataType e;
int i = 0, j = 0;
InitStack(&S);
ch = str[i];
i++;
while (ch!='\0')
{
switch (ch)
{
case '(':
PushStack(S, ch);
break;
case ')':
while (GetTop(S,&e)&&e!='(')
{
PopStack(S, &e);
exp[j] = e;
j++;
exp[j] = ' ';
j++;
}
PopStack(S, &e);
break;
case '+':
case '-':
while (!StackEmpty(S)&&GetTop(S,&e)&&e!='(')
{
PopStack(S, &e);
exp[j] = e;
j++;
exp[j] = ' ';
j++;
}
PushStack(S, ch);
break;
case '*':
case '/':
while (!StackEmpty(S)&&GetTop(S,&e)&&e=='/'||e=='*')
{
PopStack(S, &e);
exp[j] = e;
j++;
exp[j] = ' ';
j++;
}
PushStack(S, ch);
break;
default:
while (ch>='0'&&ch<='9')
{
exp[j] = ch;
j++;
ch = str[i];
i++;
}
i--;
exp[j] = ' ';
j++;
/*break;*/
}
ch = str[i];
i++;
}
while (!StackEmpty(S))
{
PopStack(S, &e);
exp[j] = e;
j++;
exp[j] = ' ';
j ++;
}
exp[j] = '\0';
}
結果: