標籤:
1,中綴運算式的定義及為什麼要將中綴運算式轉換為尾碼運算式?
中綴運算式(中綴記法)
中綴運算式是一種通用的算術或邏輯公式表示方法,操作符以中綴形式處於運算元的中間。中綴運算式是人們常用的算術表示方法。
雖然人的大腦很容易理解與分析中綴運算式,但對電腦來說中綴運算式卻是很複雜的,因此計算運算式的值時,通常需要先將中綴運算式轉換為首碼或尾碼運算式,然後再進行求值。對電腦來說,計算首碼或尾碼運算式的值要比中綴運算式簡單。
比如,電腦計算尾碼運算式的過程如下----尾碼運算式的電腦求值:
從左至右掃描運算式,遇到數字時,將數字壓入堆棧,遇到運算子時,彈出棧頂的兩個數,用運算子對它們做相應的計算(次棧頂元素 op 棧頂元素),並將結果入棧;重複上述過程直到運算式最右端,最後運算得出的值即為運算式的結果。
例如尾碼運算式“3 4 + 5 × 6 -”:
(1) 從左至右掃描,將3和4壓入堆棧;
(2) 遇到+運算子,因此彈出4和3(4為棧頂元素,3為次頂元素,注意與首碼運算式做比較),計算出3+4的值,得7,再將7入棧;
(3) 將5入棧;
(4) 接下來是×運算子,因此彈出5和7,計算出7×5=35,將35入棧;
(5) 將6入棧;
(6) 最後是-運算子,計算出35-6的值,即29,由此得出最終結果。
2,中綴運算式轉換為尾碼運算式演算法:
這裡只用了一個棧來儲存掃描中綴運算式時遇到的運算子。掃描過程中運算的運算元則直接 append 到輸出運算式的末尾
?運算子在何種情況下壓入棧?
若當前掃描的運算子的優先順序大於棧頂運算子的優先順序,則進行入棧。
若當前掃描的運算子的優先順序與棧頂運算子的優先順序相同,則需要判斷當前掃描的運算子運算時的結合方向,若結合方向為從左至右,則不需要入棧;若結合方向為從右至左,則入棧。其中,加、減、乘、除 運算子的結合方向為從左至右,而求冪運算子的結合方向為從右至左。由於求冪運算子的最優級最高且它的結合方向為從右至左,故掃描遇到求冪運算子時直接將其入棧。
?對於中綴運算式中的括弧的處理
左括弧總是被壓入棧。一旦左括弧在棧中,就被當作優先順序最低的運算子來對待,即:任何一個後繼的運算子都將被壓入棧。在遇到一個右括弧時,從棧中彈出運算子並將它們添加到輸出運算式末尾,直至彈出一個左括弧為止(尾碼運算式中沒有括弧,當然括弧也就不需要添加到輸出運算式了)。然後,演算法再繼續....
在從左向右處理中綴運算式的過程中,根據遇到的符號,執行下列動作:
①運算元 每個運算元都添加到輸出運算式末尾(輸出運算式就是最終得到的尾碼運算式結果)
②運算子 ^(求冪運算) ^ 壓入棧(因為在所有的運算子中(加、減、乘、除、求冪)求冪運算的優先順序最高,且求冪運算的結合方式為從右至左)
③運算子 + - * / 從棧中彈出運算子,並將它們添加到輸出運算式末尾,直至棧空或者棧頂優先順序比新的運算子低,然後再將新的運算子壓入棧
④左括弧 ( 壓入棧
⑤右括弧 ) 從棧中彈出運算子,將它們添加到輸出運算式末尾,直至彈出一個左括弧,丟棄這兩個括弧
3,具體的中綴運算式轉尾碼運算式的JAVA代碼實現
注意:程式中用來存放操作符的棧 不是 JDK 中java.util 包的Stack,而是自己實現的Stack。參考:使用JAVA數組實現順序棧
1 import list.SequenceStack; 2 import list.Stack; 3 4 public class Postfix { 5 /* 6 * @Task: 將中綴運算式轉換為尾碼運算式 7 * @param: infix 合法的中綴運算式字串 8 * @return: 與infix等價的尾碼運算式字串 9 */10 public static String convert2Postfix(String infix){11 StringBuffer postfix = new StringBuffer();//初始化一個字串緩衝區存放轉換過程中產生的尾碼運算式12 Stack<Character> operatorStack = new SequenceStack<Character>();13 int characterCount = infix.length();14 char topCharactor;15 16 for(int index = 0; index < characterCount; index++){17 boolean done = false;18 char nextCharacter = infix.charAt(index);19 if(isVariable(nextCharacter))20 postfix = postfix.append(nextCharacter);21 else{22 switch(nextCharacter)23 {24 case ‘^‘:25 operatorStack.push(nextCharacter);26 break;27 case ‘+‘: case ‘-‘: case ‘*‘: case ‘/‘:28 while(!done && !operatorStack.empty()){29 topCharactor = operatorStack.peek();30 if(getPrecedence(nextCharacter) <= getPrecedence(topCharactor)){31 postfix = postfix.append(topCharactor);32 operatorStack.pop();33 }34 else35 done = true;//當棧頂元素逐漸pop後,nextCharacter的優先順序大於 棧頂的優先順序36 }//end while37 operatorStack.push(nextCharacter);//當nextCharacter的優先順序大於 棧頂的優先順序,再把nextCharacter push 入棧38 break;39 case ‘(‘:40 operatorStack.push(nextCharacter);41 break;42 case ‘)‘:43 topCharactor = operatorStack.pop();44 while(topCharactor != ‘(‘){45 postfix = postfix.append(topCharactor);46 topCharactor = operatorStack.pop();47 }48 break;49 default:break;50 }//end switch51 }52 }//end for53 54 while(!operatorStack.empty()){55 topCharactor = operatorStack.pop();56 postfix = postfix.append(topCharactor);57 }58 return postfix.toString();59 }60 61 private static int getPrecedence(char operator){62 switch(operator)63 {64 case ‘(‘: case ‘)‘: return 0;//實際只有 + - * / 才需要調用該函數比較優先順序65 case ‘+‘: case ‘-‘: return 1;// + - 優先順序為166 case ‘*‘: case ‘/‘: return 2;// * / 優先順序為267 case ‘^‘: return 3;68 }69 return -1;70 }71 72 //運算元預設用字母來表示73 private static boolean isVariable(char charactor){74 return Character.isLetter(charactor);75 }76 77 //for test purpose78 public static void main(String[] args) {79 String postfix = convert2Postfix("a/b*(c +(d-e))");80 System.out.println(postfix);81 }82 }
參考資料:首碼、中綴、尾碼運算式
演算法詳細解釋參考《資料結構與演算法JAVA語言描述第二版》--Frank M.Carrano 著.第21章
棧的應用之中綴運算式轉尾碼運算式