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 else 35 done = true;//當棧頂元素逐漸pop后,nextCharacter的優先級大於 棧頂的優先級 36 }//end while 37 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 switch 51 } 52 }//end for 53 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;// + - 優先級為1 66 case '*': case '/': return 2;// * / 優先級為2 67 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 purpose 78 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章