編寫程序,將任意一個合法的中綴表達式轉換成逆波蘭式。
【問題描述】表達式計算是實現程序設計語言的基本問題之一。在計算機中進行算術表達式的計算可通過棧來實現。通常書寫的算術表達式由操作數、運算符以及圓括號連接而成。為簡便起見,本題只討論雙目運算符。
算術表達式的兩種表示如下:
⑴中綴表達式:把雙目運算符出現在兩個操作數中間的表示,稱為算術表達式的中綴表示。中綴表示的算術表達式,稱為中綴算術表達式,也稱中綴表達式。如表達式2+5*6就是中綴表達式。
⑵后綴表達式:中綴表達式的計算比較復雜。能否把中綴表達式轉換成另一種形式的表達式,使計算簡單化呢?波蘭科學家盧卡謝維奇(Lukasiewicz)提出了算術表達式的另一種表示,即后綴表式,又稱逆波蘭式。
逆波蘭式即是將算術表達式用后綴方法表示,即,把運算符放在兩個運算對象的后面。逆波蘭式也稱后綴算術表達式,或后綴表達式。在逆波蘭式中,不存在括號,也不存在優先級的差別,計算過程完全按運算符出現的先后次序進行,整個計算過程僅需一遍掃描便可完成,比中綴表達式的計算簡單。
例如,12!4!-!5!/就是一個逆波蘭式。其中’!’表示操作數間的空格,因減法運算符在前,除法運算符在后,所以應先做減法,后做除法;減法的兩個操作數是它前面的12和4,其中第一個數12是被減數,第二個數4是減數;除法的兩個操作數是它前面的12減4的差(即8)和5,其中8是被除數,5是除數。
請查閱中綴表達式轉換成對應的后綴算術表達式的規則,完成本題。 表2是一些中綴表達式與后綴表達式對應的例子:
表1 中綴表達式與對應的逆波蘭式 |
|
中綴表達式 |
后綴表達式 |
3/5+6 |
3!5!/!6!+ |
16-9*(4+3) |
16!9!4!3!+!*!- |
2*(x+y)/(1-x) |
2!x!y!+!*!1!x!-!/ |
(25+x)*(a*(a+b)+b) |
25!x!+!a!a!b!+!*!b!+!* |
【假設條件】本題應對輸入的中綴表達式,輸出其對應的逆波蘭式。假定表達式一定是合法的,且其中的數字均為1位整數,運算符包括:+,-,*,/,(,)。輸入輸出均為字符串形式。可以如下形式實現:void InfixToPostfix(char *infix, char *posfix);
數據結構的周作業,應該不會有同學看到這篇文章趴hhh
雖然中綴表達式符合人們的日常習慣,但是在計算機中,為了方便計算表達式的值,一般都是采用前綴表達式或者后綴表達式,所以就需要我們能夠將中綴表達式進行相應的轉換,另外在題目中已經對后綴表達式進行了詳細的闡述,這里不再贅述。
需要提到的是:算術表達式中由三個部分組成,操作數,運算符和圓括號。在中綴表達式中有時括號是必需的。計算過程中必須用括號將操作符和對應的操作數括起來,用於指示運算的次序。而在前綴表達式和后綴你表達式中不會存在括號,運算符都按照其優先級進行了相應的排序.
如果不儲存最后的后綴表達式,我們在掃描字符串的時候直接將其輸出的話,就只需要用到一個棧來儲存運算符。
轉換的過程如下,慢慢思考的話整個過程還是比較簡單的:
1,初始化儲存運算符的棧S1
2,從左往右掃描中綴表達式
3,遇到操作數的時候,將其輸出
4,遇到運算符的時候,比較其與S1棧頂運算符的優先級
1,如果S1為空,或者棧頂運算符為左括號"(",直接將此運算符入棧
2,否則如果優先級比棧頂運算符高,也將該運算符壓入S1,注意沒有相等
3,否則將S1棧頂的運算符彈出並輸出,再次轉到4.1,令運算符與棧頂新的運算符進行比較
5,遇到括號:
1,如果是左括號,直接壓入S1
2,如果是右括號,舍棄右括號,並依次彈出S1棧頂的運算符,直到遇到左括號,再將左括號舍棄
6,重復步驟2-->5,直到表達式最右邊
7,將S1中剩余的運算符依次彈出並輸出,最后顯示的就是轉換后的逆波蘭式(后綴表達式)
接下來是代碼實現,按照上面的分析步驟進行代碼編寫即可:
1,初始化儲存運算符的棧S1:
typedef struct{ int top; char ch[100]; }Stack; #define LEN sizeof(Stack) Stack *init(){ Stack* stack=(Stack*)malloc(LEN); stack->top=0; return stack; }
用數組來模擬棧,初始化空間為ch[100],棧頂指針top最開始設置為0
2,從左往右掃描中綴表達式
for(i=0;i<strlen(infix);)
這里的infix數組是輸入的中綴表達式字符串,沒有設置i++的原因是分情況進行討論,避免i重復累加
3,遇到操作數的時候,將其輸出
if(infix[i]>='0'&&infix[i]<='9'){ while(infix[i]>='0'&&infix[i]<='9'){ printf("%c",infix[i]); i++; } printf("!"); }
我們不再使用另一個隊列或數組對其結果進行儲存,而是直接將符合的結果直接輸出,節省了空間並且簡化操作,輸出獨立的數字之后再輸出"!"進行分割
4,遇到運算符的時候,比較其與S1棧頂運算符的優先級
這里與棧頂運算符優先級的比較,為了避免重復寫代碼,我們將其模塊化寫成check()函數,傳入兩個棧頂運算符和當前運算符進行優先級比較。
當前運算符的優先級比棧頂運算符的優先級高的時候,我們就令其入棧,而仔細思考之后只有一種情況,也就是棧頂運算符為"+,-,("的時候,並且當前運算符為"/ *"的時候,才會入棧,其余情況是相等或者低於,就不用入棧,直接彈出棧頂元素,然后用新的棧頂元素繼續比較
bool check(char a,char b){ //a為棧頂元素,b為當前元素 //返回true時,將當前元素壓入棧中, 即當前運算符比棧頂運算符的優先級高 //返回false時 ,即當前運算符與棧頂運算符的優先級相等或低 if(a=='('){ return true; } if((a=='+'||a=='-')&&(b=='*'||b=='/')){ return true; }else{ return false; } }
5,遇到括號:
括號分為左括號和右括號,左括號直接入棧,右括號的話直接拋棄,並且彈出棧中左括號上面的所有運算符,再拋棄左括號
if(infix[i]==')'){ while(stack->ch[stack->top]!='('){ pop(stack); if(stack->top>0||i<strlen(infix)){ printf("!"); } } stack->top--; i++; }else if(stack->top==0||infix[i]=='('){ push(stack,infix[i]); i++; }
6,重復步驟2-->5,直到表達式最右邊
第六步直接在for循環里面完成了,就不用再說
7,將S1中剩余的運算符依次彈出並輸出,最后顯示的就是轉換后的逆波蘭式(后綴表達式)
while(stack->top){ pop(stack); if(stack->top>0||i<strlen(infix)){ printf("!"); } }
然后是完整代碼:
#include<stdio.h> #include<stdlib.h> #include<string.h> typedef struct{ int top; char ch[100]; }Stack; int flag=1; #define LEN sizeof(Stack) Stack *init(){ Stack* stack=(Stack*)malloc(LEN); stack->top=0; return stack; } void push(Stack* stack,char ch){ stack->top++; stack->ch[stack->top]=ch; } void pop(Stack* stack){ if(stack->top==0){ return; } printf("%c",stack->ch[stack->top]); stack->top--; } bool check(char a,char b){ //a為棧頂元素,b為當前元素 //返回true時,將當前元素壓入棧中, 即當前運算符比棧頂運算符的優先級高 //返回false時 ,即當前運算符與棧頂運算符的優先級相等或低 if(a=='('){ return true; } if((a=='+'||a=='-')&&(b=='*'||b=='/')){ return true; }else{ return false; } } void InfixToPostfix(char *infix){ Stack *stack=init(); int i; for(i=0;i<strlen(infix);){ if(infix[i]>='0'&&infix[i]<='9'){ while(infix[i]>='0'&&infix[i]<='9'){ printf("%c",infix[i]); i++; } printf("!"); } if(infix[i]==')'){ while(stack->ch[stack->top]!='('){ pop(stack); if(stack->top>0||i<strlen(infix)){ printf("!"); } } stack->top--; i++; }else if(stack->top==0||infix[i]=='('){ push(stack,infix[i]); i++; }else if(check(stack->ch[stack->top],infix[i])){ push(stack,infix[i]); i++; }else{ pop(stack); if(stack->top>0||i<strlen(infix)){ printf("!"); } } } while(stack->top){ pop(stack); if(stack->top>0||i<strlen(infix)){ printf("!"); } } } int main(){ char infix[100]="3/5+6"; InfixToPostfix(infix); return 0; }
主函數里面的infix是我們輸入的字符串,當其中綴表達式為:16-9*(4+3) 的時候,輸出的結果為:
再測試一下題目里面的3/5+6式子,輸出結果為:
對於這種式子是會失敗的:2*(x+y)/(1-x)
從前面的代碼也可以看出來,我們判斷數字是0--9之間,但是這里是x,y,所以判斷不起作用,但是題目里面是:
給出的是一位整數,即合法的式子,我們就能夠得到正確的結果,以上
參考博客: