我們平常書寫的四則運算表達式屬於中綴表達式,形式為"9+(3-1)*3+10/2",因為所有的運算符號都在兩操作數之間,所以稱為中綴表達式。我們使用中綴表達式來計算表達式的值,不過這種形式並不適合計算機求解。接下來,我們將中綴表達式轉化為后綴表達式,所謂的后綴表達式就是操作符位於操作數后面的不包含括號的算數表達式,也叫做逆波蘭表達式。
1)首先介紹一種人工的轉化方法(http://www.cnblogs.com/MichaelYin/archive/2012/05/02/2479248.html)。以"9+(3-1)*3+10/2"為例,按照運算的規則,找出首先計算的部分,這部分包含兩個操作數和一個操作符,將操作符移動到兩個操作數右側,這就完成了第一部分的轉換,將這部分看作一個操作數,按照運算規則,以相同的方法轉換,轉換過程如下:
2)還可以利用二叉樹求得后綴表達式,首先利用中綴表達式構造二叉樹,數字是葉子節點,操作符為根節點。每次找到“最后計算”的運算符,作為當前根節點,運算符左側表達式作為左節點,右側表達式作為右節點,然后遞歸處理(http://www.xuebuyuan.com/388108.html)。9+(3-1)*3+10/2對應的二叉樹的構造過程如下圖所示:
此二叉樹做后序遍歷就得到了后綴表達式。對應代碼:http://www.davex.pw/2016/03/21/How-to-make-a-Expression-Tree/
3)還可以利用棧來實現中綴表達式轉化為后綴表達式。轉化方法如下所述:
a.從左向右掃描表達式,如果是數字就輸出,否則轉b。
b.如果當前掃描的字符是")",則棧頂元素出棧並輸出一直到棧頂元素為"(",然后刪除棧頂元素"(",並不輸出。
c.如果掃描的字符或者棧頂元素是“(”,掃描的字符直接入棧。即使掃描的字符是")"也不會入棧,因為如果是")",會出棧至棧頂元素是"("。
d.如果掃描字符是"+"或者"-",則一直出棧至棧頂元素為"+"或者"-"或者"("。如果最終棧頂元素是"(",則掃描操作符直接入棧,否則,彈出棧頂元素,然后掃描的操作符再入棧。
e.如果掃描的操作符是"*"或者"/",如果棧頂元素是同優先級的"*"或者"/",首先將棧頂元素出棧,然后掃描的操作符入棧。否則,直接入棧。
f.掃描完成整個表達式之后,如果棧內還有元素,則依次全部出棧。
得到后綴表達式之后,從左至右掃描得到的后綴表達式,如果是數字,直接入棧,如果是運算符,則依次彈出兩個棧頂元素,最先彈出的元素是右操作數,較后彈出的是左操作數,兩操作數使用操作符計算結果結果再入棧,掃描結束時,棧中只有一個元素,就是計算結果。Java中double型變量直接進行加減乘除運算,會有精度損失,所以需要使用math包下的BigDecimal進行運算。
對應的代碼如下,首先是棧的定義:

1 import java.math.BigDecimal; 2 import java.util.*; 3 4 /** 5 * Created by hfz on 2016/7/30. 6 */ 7 public class Stack_Array implements Stack { 8 public static int CAPACITY=40; 9 protected int capacity; 10 protected int top=-1; 11 protected Object[] S; 12 public Stack_Array(int capacity){ 13 this.capacity=capacity; 14 S=new Object[capacity]; 15 } 16 public Stack_Array(){ 17 this(CAPACITY);//一般情況下,使用this.方法名調用自身方法,不過在構造方法里可以使用this()的形式調用其他構造函數 18 } 19 public void push(Object ele) throws ExceptionStackFull { 20 if(getSize()==CAPACITY){ 21 throw new ExceptionStackFull("棧溢出!"); 22 } 23 S[++top]=ele; 24 } 25 public Object pop() throws ExceptionStackEmpty{ 26 if(isEmpty()){ 27 throw new ExceptionStackEmpty("棧為空,不能出棧!"); 28 } 29 Object ele=S[top]; 30 S[top--]=null; 31 return ele; 32 } 33 34 public Object top() throws ExceptionStackEmpty{ 35 if(isEmpty()){ 36 throw new ExceptionStackEmpty("棧為空,沒有棧頂元素!"); 37 } 38 return S[top]; 39 } 40 public boolean isEmpty(){ 41 return (top<0); 42 } 43 public int getSize(){ 44 return top+1; 45 } 46 public static void main(String [] args){ 47 String s="9 + ( 3 - 1 ) * 3 + 10 / 2"; 48 String s1="20 * ( ( 2.44 - 1.8 ) / 0.4 + 0.15 )"; 49 50 System.out.println(reversePlishNotation2(s)); 51 System.out.println(reversePlishNotation2(s1)); 52 53 }
stack是接口,定義如下:

1 import java.util.Objects; 2 3 /** 4 * Created by hfz on 2016/7/30. 5 * 棧的基本功能有兩個: 6 * 入棧:push(X) 7 * 出棧:pop() 8 * 還可以增加其他的功能: 9 * 獲得棧頂元素:top() 10 * 判斷棧是否為空:isEmpty() 11 * 獲得棧中元素個數:getSize() 12 * 13 * 14 * 15 */ 16 public interface Stack { 17 void push(Object ele); 18 Object pop() throws ExceptionStackEmpty;//刪除棧頂元素並返回 19 Object top() throws ExceptionStackEmpty;//返回棧頂元素,不刪除。 20 boolean isEmpty(); 21 int getSize(); 22 } 23 class ExceptionStackEmpty extends RuntimeException{ 24 public ExceptionStackEmpty(String err){ 25 super(err);//調用父類只有一個String參數的構造函數 26 } 27 }
然后是reversePlishNotation2轉換方法,首先利用棧將中綴表達式轉化為后綴表達式,然后利用棧計算后綴表達式的值,表達式輸入格式為字符串,操作數和運算符以及小括號以空格分隔:

1 /* 2 中綴表達式以#作為結束標志,以空格分割各個元素。 3 中綴表達式轉化為后綴表達式(逆波蘭表達式)的方法 4 1.從左向右掃描中綴表達式,遇到數字就直接輸出。遇到運算符,如果其優先級高於棧頂元素或棧為空,入棧。如果低於棧頂 5 元素優先級,則棧頂元素出棧輸出,一直到棧頂元素的優先級低於或等於此運算符。如果兩個運算符優先級相同的話,彈出棧頂元素,然后再將運算符入棧。 6 7 2.對於小括號做另外處理,如果是左括號“(”,直接入棧。右括號")"的話,棧頂元素出棧輸出,一直到棧頂元素為“(”,將“(”出棧 8 ,不過並不輸出。右括號“)”並不入棧,直接丟棄。 9 3.掃描完成之后,如果棧中還有元素,依次輸出。 10 */ 11 12 13 14 public static String reversePlishNotation2(String str){ 15 Stack_Array notationStack = new Stack_Array(); 16 String[] expressionString=str.split(" "); 17 int expressionLength = expressionString.length; 18 String currentString = "0"; 19 StringBuilder sb = new StringBuilder(); 20 Set<String> notationSet=new HashSet<>(); 21 notationSet.add("+"); 22 notationSet.add("-"); 23 notationSet.add("*"); 24 notationSet.add("/"); 25 notationSet.add("("); 26 notationSet.add(")"); 27 String topElement="null"; 28 for (int i = 0; i <expressionLength ; i++) { 29 currentString=expressionString[i]; 30 if(!notationSet.contains(currentString)){ 31 sb.append(currentString).append(" "); 32 } 33 else{//不是數字,而是操作符 34 try{ 35 topElement=(String)notationStack.top(); 36 } 37 catch (ExceptionStackEmpty ex){ 38 notationStack.push(currentString); 39 continue; 40 } 41 if(currentString.equals(")")){//當前掃描的字符是")",則操作符出棧至棧頂元素是"(",同時添加操作符,最后刪除"("。 42 while(!topElement.equals("(")) { 43 sb.append(topElement).append(" "); 44 try { 45 notationStack.pop(); 46 topElement = (String) notationStack.top(); 47 } 48 catch (ExceptionStackEmpty ex){ 49 break; 50 } 51 } 52 if(topElement.equals("(")){ 53 notationStack.pop(); 54 } 55 } 56 else if(currentString.equals("(")||topElement.equals("(")){//如果掃描字符或者棧頂字符是"(",則掃描字符直接入棧。 57 // 即使掃描字符是")"也不會入棧,因為上面一層的判斷條件,要求出棧至"("。 58 notationStack.push(currentString); 59 } 60 else if(currentString.equals("+")||currentString.equals("-")){ 61 //如果掃描字符是"+"或者"-",則一直出棧至棧頂元素為"+"或者"-"或者"("。如果最終棧頂元素是"(",則掃描操作符直接入棧,否則,彈出棧頂 62 //元素,然后掃描的操作符再入棧。 63 while (!topElement.equals("+")&&!topElement.equals("-")&&!topElement.equals("(")){ 64 sb.append(topElement).append(" "); 65 try{ 66 notationStack.pop(); 67 topElement=(String)notationStack.top(); 68 } 69 catch (ExceptionStackEmpty ex){ 70 break; 71 } 72 } 73 if(topElement.equals("+")||topElement.equals("-")){ 74 sb.append((String)notationStack.pop()).append(" "); 75 } 76 notationStack.push(currentString); 77 } 78 else if(currentString.equals("*")||currentString.equals("/")){ 79 //如果掃描的操作符是"*"或者"/",如果棧頂元素是同優先級的"*"或者"/",首先將棧頂元素出棧,然后掃描的操作符入棧。否則,直接入棧。 80 if(topElement.equals("*")||topElement.equals("/")){ 81 sb.append((String) notationStack.pop()).append(" "); 82 } 83 notationStack.push(currentString); 84 } 85 } 86 } 87 //掃描完成整個表達式之后,如果棧內還有元素,則依次全部出棧。 88 while (!notationStack.isEmpty()){ 89 sb.append(notationStack.pop()).append(" "); 90 } 91 //已經得到后綴表達式。 92 93 /* 94 從左至右掃描得到的后綴表達式,如果是數字,直接入棧,如果是運算符,則依次彈出兩個棧頂元素,最先彈出的元素是右操作數,較后彈出的是左操作數,兩操作數使用操作符計算結果 95 結果再入棧,掃描結束時,棧中只有一個元素,就是計算結果。 96 Java中double型變量直接進行加減乘除運算,會有精度損失,所以需要使用math包下的BigDecimal進行運算。 97 */ 98 99 String[] plishNotation=sb.toString().split(" "); 100 Stack_Array plishExpressionStack=new Stack_Array(); 101 BigDecimal leftValue,rightValue;//Java中double型變量直接進行加減乘除運算,會有精度損失,所以需要使用math包下的BigDecimal進行運算。 102 String resultValue="null"; 103 for(String str1:plishNotation){ 104 if(notationSet.contains(str1)){ 105 rightValue=new BigDecimal((String)plishExpressionStack.pop()); 106 leftValue=new BigDecimal((String)plishExpressionStack.pop()); 107 switch (str1){ 108 case "+": 109 resultValue=leftValue.add(rightValue).toString(); 110 break;//break結束switch語句,直接跳至switch后的下句語句。 111 case "-": 112 resultValue=leftValue.subtract(rightValue).toString(); 113 break; 114 case "*": 115 resultValue=leftValue.multiply(rightValue).toString(); 116 break; 117 case "/": 118 resultValue=leftValue.divide(rightValue).toString(); 119 break; 120 } 121 plishExpressionStack.push(resultValue); 122 } 123 else{ 124 plishExpressionStack.push(str1); 125 } 126 } 127 return (String)plishExpressionStack.pop(); 128 }
4)調度場算法(http://hczhcz.github.io/2014/03/07/shunting-yard-algorithm-3.html)與上述思路類似,只是不再轉化為后綴表達式,實際上轉化為后綴表達式的過程就用了調度廠的思想。使用調度廠算法求解算數表達式的值時,定義兩個棧,分別是數字棧和符號棧。從左向右掃描算數表達式,如果掃描到數字,就將其存入數字棧。否則,就存入符號棧。每次符號棧有元素出棧時(“(”出棧除外),數字棧也出棧兩個元素,先出棧的元素為右操作數,后出棧的元素為左操作數,兩者結合運算符運算之后,將結果存入數字棧。最終,表達式掃描完成之后,如果符號棧不為空,則出棧,數字棧也同時出棧兩個元素,直至符號棧為空。
代碼如下,部分重復代碼可以改寫成函數:

1 public static String shuntingYardAlgorithm(String str){ 2 Stack_Array notationStack = new Stack_Array(); 3 Stack_Array numberStack=new Stack_Array(); 4 BigDecimal leftValue,rightValue;//Java中double型變量直接進行加減乘除運算,會有精度損失,所以需要使用math包下的BigDecimal進行運算。 5 String resultValue="null"; 6 String[] expressionString=str.split(" "); 7 int expressionLength = expressionString.length; 8 String currentString = "0"; 9 StringBuilder sb = new StringBuilder(); 10 Set<String> notationSet=new HashSet<>(); 11 notationSet.add("+"); 12 notationSet.add("-"); 13 notationSet.add("*"); 14 notationSet.add("/"); 15 notationSet.add("("); 16 notationSet.add(")"); 17 String topElement="null"; 18 for (int i = 0; i <expressionLength ; i++) { 19 currentString=expressionString[i]; 20 if(!notationSet.contains(currentString)){ 21 //sb.append(currentString).append(" "); 22 numberStack.push(currentString); 23 } 24 else{//不是數字,而是操作符 25 try{ 26 topElement=(String)notationStack.top(); 27 } 28 catch (ExceptionStackEmpty ex){ 29 notationStack.push(currentString); 30 continue; 31 } 32 if(currentString.equals(")")){//當前掃描的字符是")",則操作符出棧至棧頂元素是"(",同時添加操作符,最后刪除"("。 33 34 //字符棧出棧時,數字棧同時也彈出兩個元素,將運算結果存入數字棧。 35 36 37 while(!topElement.equals("(")) { 38 rightValue=new BigDecimal((String)numberStack.pop()); 39 leftValue=new BigDecimal((String)numberStack.pop()); 40 switch (topElement){ 41 case "+": 42 resultValue=leftValue.add(rightValue).toString(); 43 break;//break結束switch語句,直接跳至switch后的下句語句。 44 case "-": 45 resultValue=leftValue.subtract(rightValue).toString(); 46 break; 47 case "*": 48 resultValue=leftValue.multiply(rightValue).toString(); 49 break; 50 case "/": 51 resultValue=leftValue.divide(rightValue).toString(); 52 break; 53 } 54 numberStack.push(resultValue); 55 try { 56 notationStack.pop(); 57 topElement = (String) notationStack.top(); 58 } 59 catch (ExceptionStackEmpty ex){ 60 break; 61 } 62 } 63 if(topElement.equals("(")){ 64 notationStack.pop(); 65 } 66 } 67 else if(currentString.equals("(")||topElement.equals("(")){//如果掃描字符或者棧頂字符是"(",則掃描字符直接入棧。 68 // 即使掃描字符是")"也不會入棧,因為上面一層的判斷條件,要求出棧至"("。 69 notationStack.push(currentString); 70 } 71 else if(currentString.equals("+")||currentString.equals("-")){ 72 //如果掃描字符是"+"或者"-",則一直出棧至棧頂元素為"+"或者"-"或者"("。如果最終棧頂元素是"(",則掃描操作符直接入棧,否則,彈出棧頂 73 //元素,然后掃描的操作符再入棧。 74 75 //字符棧出棧時,數字棧同時也彈出兩個元素,將運算結果存入數字棧。 76 77 while (!topElement.equals("+")&&!topElement.equals("-")&&!topElement.equals("(")){ 78 try{ 79 if( ((String)notationStack.pop()).equals("*")){ 80 rightValue=new BigDecimal((String)numberStack.pop()); 81 leftValue=new BigDecimal((String)numberStack.pop()); 82 resultValue=leftValue.multiply(rightValue).toString(); 83 } 84 else{ 85 rightValue=new BigDecimal((String)numberStack.pop()); 86 leftValue=new BigDecimal((String)numberStack.pop()); 87 resultValue=leftValue.divide(rightValue).toString(); 88 } 89 numberStack.push(resultValue); 90 topElement=(String)notationStack.top(); 91 } 92 catch (ExceptionStackEmpty ex){ 93 break; 94 } 95 } 96 if(topElement.equals("+")||topElement.equals("-")){ 97 98 //字符棧出棧時,數字棧同時也彈出兩個元素,將運算結果存入數字棧。 99 100 if( ((String)notationStack.pop()).equals("+")){ 101 rightValue=new BigDecimal((String)numberStack.pop()); 102 leftValue=new BigDecimal((String)numberStack.pop()); 103 resultValue=leftValue.add(rightValue).toString(); 104 105 } 106 else{ 107 rightValue=new BigDecimal((String)numberStack.pop()); 108 leftValue=new BigDecimal((String)numberStack.pop()); 109 resultValue=leftValue.subtract(rightValue).toString(); 110 } 111 numberStack.push(resultValue); 112 } 113 notationStack.push(currentString); 114 } 115 else if(currentString.equals("*")||currentString.equals("/")){ 116 //如果掃描的操作符是"*"或者"/",如果棧頂元素是同優先級的"*"或者"/",首先將棧頂元素出棧,然后掃描的操作符入棧。否則,直接入棧。 117 118 //字符棧出棧時,數字棧同時也彈出兩個元素,將運算結果存入數字棧。 119 120 if(topElement.equals("*")||topElement.equals("/")){ 121 if( ((String)notationStack.pop()).equals("*")){ 122 rightValue=new BigDecimal((String)numberStack.pop()); 123 leftValue=new BigDecimal((String)numberStack.pop()); 124 resultValue=leftValue.multiply(rightValue).toString(); 125 126 } 127 else{ 128 rightValue=new BigDecimal((String)numberStack.pop()); 129 leftValue=new BigDecimal((String)numberStack.pop()); 130 resultValue=leftValue.divide(rightValue).toString(); 131 } 132 numberStack.push(resultValue); 133 } 134 notationStack.push(currentString); 135 } 136 } 137 } 138 139 //最終,表達式掃描完成之后,如果符號棧不為空,則出棧,數字棧也同時出棧兩個元素,直至符號棧為空。 140 141 while(!notationStack.isEmpty()){ 142 topElement=(String)notationStack.pop(); 143 rightValue=new BigDecimal((String)numberStack.pop()); 144 leftValue=new BigDecimal((String)numberStack.pop()); 145 switch (topElement){ 146 case "+": 147 resultValue=leftValue.add(rightValue).toString(); 148 break;//break結束switch語句,直接跳至switch后的下句語句。 149 case "-": 150 resultValue=leftValue.subtract(rightValue).toString(); 151 break; 152 case "*": 153 resultValue=leftValue.multiply(rightValue).toString(); 154 break; 155 case "/": 156 resultValue=leftValue.divide(rightValue).toString(); 157 break; 158 } 159 numberStack.push(resultValue); 160 } 161 return (String)numberStack.pop(); 162 }