字首中綴字尾表示式及其互相轉換
計算機中字首中綴字尾表示式是資料結構棧的重要應用,他們都是對錶達式的記法,只是相對位置不一樣,顧名思義,字首表示式指的是符號都在運算元之前,中綴表示式指的是運算子都在運算元之後,而後綴表示式是之後。如:
(a+b)*c-d是中綴表示式
-*+abcd是字首表示式
ab+c*d-是字尾表示式
中綴表示式是我們常用也是人常見的一種記法,但對於計算機來說處理很複雜,一般都是先轉換成字首或者字尾表示式再進行計算。
一、利用字尾表示式求值:
計算機利用字尾表示式求值過程可分析如下:
以中綴(3+4)*5-6為例,轉換成字尾是34+5*6-
從左向右掃描,遇到數字時,將數字壓入棧,遇到運算子時候,把棧內前兩個數字彈出,進行相應運算,並將計算結果入棧。重複直到表示式右端。
從左向右掃描。將數字3,4入棧;
遇見運算子+,彈出3,4,計算得7入棧
繼續掃描,將5入棧
與運算子*,將7,5彈出計算,得35入棧
繼續掃描,遇見6入棧
遇見運算子-,彈出兩個35,6,計算得29入棧
到最右端,停止,所得結果29
下面關鍵就是如何把中綴表示式轉換成字尾表示式:
1、初始化兩個棧,運算子棧S1和資料棧S2
2、從左向右掃描中綴表示式,遇見數字直接入棧S2
3、遇見運算子,將比較運算子與S1棧頂運算子優先順序
3-1、如果S1棧頂是空或者是或者是左括號則直接將遇見的運算子入棧S1
3-2、如果S1棧頂不是空也不是左括號,且遇見的運算子比S1內優先順序要高,那麼將該運算子直接入棧S1(注意轉換為字首表示式時是優先順序較高或相同,而這裡則不包括相同的情況)
3-3、如果S1棧頂是是右括號或者優先順序更大。則將S1內運算子出棧到S2裡,再轉回3-1
4、遇見括號
4-1、如果是遇見左括號,那麼左括號運算子直接入棧S1
4-2、如果是遇見右括號,那麼右括號運算子直接入棧S1
5、重複2-4,直至到表示式最右邊
6、將S1中剩餘運算子彈出到S2
7、 依次彈出S2中的元素並輸出,結果的逆序即為中綴表示式對應的字尾表示式(轉換為字首表示式時不用逆序)。
將中綴表示式exp轉換為字尾表示式,採用了一個op運算子棧,其他的用陣列儲存。
const int N=20; vector<char> inexpTopost(char a[],int n){ vector<char> res; char st[N]; int top=-1; int i=0; char c=a[i]; while(c!='\0'){ switch(c){ case '(':{top++;st[top]=c;break;} case ')':{while(st[top]!='('){res.push_back(st[top]);top--;} top--; break; } case '+': case '-':{while(top>-1&&st[top]!='('){res.push_back(st[top]);top--;} top++;st[top]=c; break; } case '*': case '/':{ while(top>-1&&st[top]!='('&&(st[top]=='*'||st[top]=='/')){res.push_back(st[top]);top--;} top++;st[top]=c; break; } default:{ while(c>='0'&&c<='9') {res.push_back(c);i++;c=a[i];}//裝入多位的整數 i--; res.push_back('#');//用標識表述數值結束 } } i++;c=a[i]; } while(top>-1){res.push_back(st[top--]);} return res; }
在計算字尾表示式過程中採用一個數值棧st:
float getkey_post(vector<char> arr){
int num_stack[N];int top1=-1;
int len=arr.size();
int i=0;
char c=arr[i];
int s=0;
while(i<len){
//if(top1-1==-1){cout<<"Out of index";return 0;}
c=arr[i];
switch(c){
case '+':{num_stack[top1-1]=num_stack[top1-1]+num_stack[top1];top1--;break;
}
case '-':{num_stack[top1-1]=num_stack[top1-1]-num_stack[top1];top1--;break;
}
case '*':{num_stack[top1-1]=num_stack[top1-1]*num_stack[top1];top1--;break;
}
case '/':{if(num_stack[top1]==0){cout<<"除數是0"<<endl;return 0;}
num_stack[top1-1]=num_stack[top1-1]/num_stack[top1];top1--;break;
}
case '#':break;
default:{ s=0;
while(c>='0'&&c<='9'){s=10*s+c-'0';i++;c=arr[i];}
top1++;num_stack[top1]=s;i--;
}
}
i++;
}
return num_stack[top1];
}
二、利用字首表示式求值
以中綴(3+4)*5-6為例,轉換成字首是-*+3456
利用字首表示式求值:從右向左搜尋,遇見數字時入棧,遇見運算子時候出棧前兩個數字3+4=7,計算好7再入棧,遇見運算子*,出棧前兩個進行計算7*5=35,再入棧,遇見運算子-,出棧進行運算35-6=29,入棧儲存結果。
將中綴表示式轉換為字首的過程:
1、初始化兩個棧,運算子棧S1和資料棧S2
2、從右向左掃描中綴表示式,遇見數字直接入棧S2
3、遇見運算子,將比較運算子與S1棧頂運算子優先順序
3-1、如果S1棧頂是空或者是或者是又括號則直接將遇見的運算子入棧S1
3-2、如果S1棧頂不是空也不是左括號,且遇見的運算子比S1內優先順序要高或者一樣,那麼將該運算子直接入棧S1。
3-3、如果S1棧頂是是左括號或者優先順序更大。則將S1內運算子出棧到S2裡,再轉回3-1
4、遇見括號
4-1、如果是遇見右括號,那麼左括號運算子直接入棧S1
4-2、如果是遇見左括號,那麼右括號運算子直接入棧S1
5、重複2-4,直至到表示式最左邊
6、將S1中剩餘運算子彈出到S2
7、 依次彈出S2中的元素並輸出,結果即為中綴表示式對應的字首表示式。
vector<char> inexpTopre(char a[],int n){
vector<char> res;
char num_stack[N];int top1=-1;
char op_stack[N];int top2=-1;
int i=n-1;
char c=a[i];
while(i>=0){
switch(c){
case ')':{top2++;op_stack[top2]=c;break;}
case '(':{while(op_stack[top2]!=')'){top1++;num_stack[top1]=op_stack[top2];top2--;}
top2--;
break;
}
case '+':
case '-':{while(top2>-1&&op_stack[top2]!=')'&&(op_stack[top2]=='*'||op_stack[top2]=='/')){top1++;num_stack[top1]=op_stack[top2];top2--;}
top2++;op_stack[top2]=c;
break;
}
case '*':
case '/':{top2++;op_stack[top2]=c;
break;
}
default:{
while(c>='0'&&c<='9'){top1++;num_stack[top1]=c;i--;c=a[i];}
top1++;num_stack[top1]='#';i++;
}
}
i--;if(i<0)break;
c=a[i];
}
while(top2>-1){top1++;num_stack[top1]=op_stack[top2--];}
for(i=top1;i>=0;i--)
res.push_back(num_stack[i]);
return res;
}
最後是根據字首序列求出最終結果:
int getvalue(char str[]){
int s=0;
int i,j;
int len=0;
for(i=0;str[i]!='\0';i++)
len++;
for(i=0,j=len-1;i<j;i++,j--)
{char t=str[i];str[i]=str[j];str[j]=t;}
i=0;
while(str[i]!='\0'){
s=s*10+str[i]-'0';
i++;
}
return s;
}
float getkey_pre(vector<char>arr){
int st[N],top=-1;
int len=arr.size();
int i=len-1,j;
char c=arr[i];
int s;
char tmp[N];
while(i>=0){
c=arr[i];
switch(c){
case '+':{st[top-1]=st[top]+st[top-1];top--;break;}
case '-':{st[top-1]=st[top]-st[top-1];top--;break;}
case '*':{st[top-1]=st[top]*st[top-1];top--;break;}
case '/':{
if(st[top-1]==0){cout<<"除數是0";return 0;}
st[top-1]=st[top]/st[top-1];top--;break;
}
case '#':break;
default:{
s=0;
j=0;
while(c>='0'&&c<='9'){tmp[j++]=c;i--;c=arr[i];}
tmp[j]='\0';s=getvalue(tmp);
top++;st[top]=s;i++;
}
}
i--;
}
return st[top];
}