咱們日常書寫的四則運算表達式屬於中綴表達式,形式爲"9+(3-1)*3+10/2",由於全部的運算符號都在兩操做數之間,因此稱爲中綴表達式。咱們使用中綴表達式來計算表達式的值,不過這種形式並不適合計算機求解。接下來,咱們將中綴表達式轉化爲後綴表達式,所謂的後綴表達式就是操做符位於操做數後面的不包含括號的算數表達式,也叫作逆波蘭表達式。html
1)首先介紹一種人工的轉化方法(http://www.cnblogs.com/MichaelYin/archive/2012/05/02/2479248.html)。以"9+(3-1)*3+10/2"爲例,按照運算的規則,找出首先計算的部分,這部分包含兩個操做數和一個操做符,將操做符移動到兩個操做數右側,這就完成了第一部分的轉換,將這部分看做一個操做數,按照運算規則,以相同的方法轉換,轉換過程以下:java
2)還能夠利用二叉樹求得後綴表達式,首先利用中綴表達式構造二叉樹,數字是葉子節點,操做符爲根節點。每次找到「最後計算」的運算符,做爲當前根節點,運算符左側表達式做爲左節點,右側表達式做爲右節點,而後遞歸處理(http://www.xuebuyuan.com/388108.html)。9+(3-1)*3+10/2對應的二叉樹的構造過程以下圖所示:git
此二叉樹作後序遍歷就獲得了後綴表達式。對應代碼:http://www.davex.pw/2016/03/21/How-to-make-a-Expression-Tree/github
3)還能夠利用棧來實現中綴表達式轉化爲後綴表達式。轉化方法以下所述:算法
a.從左向右掃描表達式,若是是數字就輸出,不然轉b。express
b.若是當前掃描的字符是")",則棧頂元素出棧並輸出一直到棧頂元素爲"(",而後刪除棧頂元素"(",並不輸出。app
c.若是掃描的字符或者棧頂元素是「(」,掃描的字符直接入棧。即便掃描的字符是")"也不會入棧,由於若是是")",會出棧至棧頂元素是"("。ide
d.若是掃描字符是"+"或者"-",則一直出棧至棧頂元素爲"+"或者"-"或者"("。若是最終棧頂元素是"(",則掃描操做符直接入棧,不然,彈出棧頂元素,而後掃描的操做符再入棧。函數
e.若是掃描的操做符是"*"或者"/",若是棧頂元素是同優先級的"*"或者"/",首先將棧頂元素出棧,而後掃描的操做符入棧。不然,直接入棧。ui
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 }