(超詳細、易懂)Java實現--表示式之中綴轉字尾
阿新 • • 發佈:2018-12-11
自打遇到棧,一直都想些寫一篇通俗易懂的將中綴表示式轉化為字尾表示式的博文,今天總算完成了這個心願。
所有的解釋在程式碼行裡面均有解釋。有什麼不懂的可以在評論區或者私信我。
為了不用一直拉動程式碼行看註釋(這回很麻煩也很浪費時間),本人將較長的註釋分成了兩行。
(對棧,還不夠了解的,可以看我的另一篇博文)
定義棧程式碼:
轉換程式碼:
/** * 將中綴表示式轉化為字尾表示式 * @author Administrator * */ public class InToPost { private String input; private String output=""; private StackX theStack; public InToPost(String in) { input = in; int stackSize = input.length(); theStack = new StackX(stackSize); } public String doTrans() { //利用for迴圈,讀取中綴表示式中的每一個字元 for(int i=0;i<input.length();i++) { //定義一個字元型變數ch,將中綴表示式的字元賦給ch char ch = input.charAt(i); //演繹字元ch是否入棧,將棧中的內容先打印出來 theStack.diaplayStack("For "+ch+" "); //利用switch-case語句,對應字元對應不同的操作 switch(ch) { case '(': //如果ch是'('的話,直接將該字元壓入棧中 theStack.push(ch); break; //如果ch是')'的話,執行gotParen操作,Paren是parenthesis的縮寫, //表示括號的意思,這裡即要實現獲取')'括號相對應的操作 case ')': gotParen(ch); break; //如果ch是'+'或者'-'的話,獲取操作Oper是Operation的縮寫,即操作的意思 case '+': case '-': gotOper(ch,1); break; //如果ch是'*'或者'/',執行gotOper方法 case '*': case '/': gotOper(ch,2); break; //如果獲取的字元既不是+-*/,也不是'('或者')',表示這個字元是數字,直接賦值給output. default: output+=ch; break; } } //當執行玩上述操作以後,棧不為空,則將棧中的元素一一賦值給output //什麼時候執行完上述操作棧依然不為空呢?有很多例子,比如說(A+B)*C //在(A+B)*C中最後的時候棧中還會有*這個符號,因為我們在ch在==')'的時候 //就把棧中裡面的內容'+'和'('[注意這裡是有順序的,先執行'+']給執行了,在遇到'('的時候 //直接pop,其實質是將top--,'('它本身還在數組裡頭,這在我們初步學習棧的時候就已經說明過了 //此時top==-1.相當於棧是空的 //遇到'*'以後,將其壓入棧中 //因為'*'後面已經沒有其它操作符[+-*/)(],所以'*'就一直儲存在棧中了 while(!theStack.isEmpty()) { theStack.diaplayStack("While "); output+=theStack.pop(); } theStack.diaplayStack("End "); //返回output,此時已經完成了將中綴表示式轉化為字尾表示式 return output; } //當遇到'(''+''-'或者'*'/'的時候執行以下操作 public void gotOper(char opThis,int prec1) { //只要棧中不為空,就要將棧頂元素拿出來, //若是'('則放回,若是'+''-''*''/'就要和棧頂元素進行比較 while(!theStack.isEmpty()) { //獲取棧頂元素 char opTop = theStack.pop(); //如果棧頂元素是'(',則將其放回棧中,因為有'(', //表示後面一定有運算操作是要先執行的,'('也可以是看做一個標誌符,或者說是分割符 if(opTop=='(') { theStack.push(opTop); //退出迴圈,繼續下一個字元的操作 break; }//否則如果不是'('的話,執行下面操作 else { //定義一個prec2,prec2代表的是棧頂元素, //目的是為了跟prec1進行比較,prec1表示的是要插入棧中元素 //如果prec2>prec1的話表示棧頂元素的優先順序比要插入棧中的元素優先順序要大, //此時我們就應該將棧頂元素賦值給output,比如棧頂元素opTop為'*' opThis為'+' //如果prec2<prec1的話,表明棧頂元素的優先順序要小於要插入棧中的優先順序, //此時我們將opTop放回棧中,比如棧頂元素opTop為'+' opThis為'*',剛好與上述情況相反 int prec2; if(opTop=='+' || opTop=='-') { prec2 = 1; }else prec2 = 2; if(prec2<prec1) { //如果棧頂元素的優先順序要小於要插入棧中的優先順序,此時我們將opTop放回棧中 theStack.push(opTop); //退出迴圈,繼續下一個字元的操作 break; }else //如果棧頂元素的優先順序要大,則將其賦值給output output += opTop; } } //總結一下上述的操作,其實就是將棧頂元素取出來,然後考慮它是否應該放回到棧中, //放回棧中有兩種情況: //1、如果遇到'(',我們就將它放回棧中 //2、如果棧頂元素opTop的優先順序要比opThis的優先順序藥效,我們將它(棧頂元素opTop)放回棧中 //賦值給output的情況只有一種: //就是棧頂元素opTop的優先順序要比opThis的要大, //此時我們直接將其賦值給output, //因為優先順序都比opThis要大了,我們當然要先執行該運算啊! //考慮完棧頂元素opTop是應該放回還是不放回以後,我們將opThis放到棧裡面去 theStack.push(opThis); //注:此方法針對的符號是 //'(' '+' '-' '*' '/',五個, //如果遇到的是')',不執行該方法,執行的是下面的方法gotOparen } //如果遇到')'我們並不將')'壓入棧中,這時候考慮的是是否將棧中元素賦值給output的問題了 public void gotParen(char ch) { //如果該棧不為空,為空的時候不可能(只要不是故意的)出現')', //因為在表示式裡面有')'理所當然就有'(' //所以不應該為空,為空根本就麼有意義去討論 while(!theStack.isEmpty()) { //獲取棧頂元素,只要棧頂元素不是'(', //則我們將其賦值給output,然後進行下一次操作,直到遇到'('為止 char chx = theStack.pop(); if(chx=='(') { break; }else { output += chx; } } } }
測試程式碼:
/** * 測試轉換 * @author Administrator * */ public class TransformApp { public static void main(String[] args) { String input = "D*(A+B)*C"; System.out.println("字首表示式:"+input); InToPost test = new InToPost(input); String output = test.doTrans(); System.out.print("字尾表示式:"+output); } }
測試結果:
字首表示式:D*(A+B)*C For D Stack (bottom-->top): For * Stack (bottom-->top): For ( Stack (bottom-->top):* For A Stack (bottom-->top):* ( For + Stack (bottom-->top):* ( For B Stack (bottom-->top):* ( + For ) Stack (bottom-->top):* ( + For * Stack (bottom-->top):* For C Stack (bottom-->top):* While Stack (bottom-->top):* End Stack (bottom-->top): 字尾表示式:DAB+*C*