棧的應用之中綴表達式轉后綴表達式


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章


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM