後綴表達式又叫逆波蘭表達式,其求值過程能夠用到棧來輔助存儲。例如要求值的後綴表達式爲:1 2 3 + 4 * + 5 -,則求值過程以下:算法
2. 接着讀到 「+」操做符,則將棧頂和次棧頂元素出棧與操做符進行運算,執行 2 + 3操做,並將結果5壓入棧中,此時棧結構以下express
3. 繼續讀到4,是數字則直接壓棧,此時棧結構以下測試
4. 繼續向後讀取,此時讀取到操做符「*」,則將棧頂和次棧頂元素出棧與操做符進行運算,即執行 5 * 4 ,而後將結果20壓入棧中,此時棧結構以下spa
5. 繼續向後讀取,此時讀到操做符「+」,則將棧頂和次棧頂元素出棧與操做符進行運算,即執行1 + 20,而後將結果21壓入棧中,此時棧結構以下3d
6. 繼續向後讀取,此時讀到數字5,則直接將數字壓棧,棧結構以下指針
7. 讀取到最後一個爲操做符,將棧頂和次棧頂元素出棧與操做符進行運算,即執行 21- 5(注意順序 次棧頂-棧頂),而後將結果16壓入棧中,此時棧結構以下code
此時棧頂元素即爲表達式的結果。(注:爲方便理解,這裏棧頂指針向下移動後,上面元素直接去掉了,實際狀況數據還會存在對應位置,只是經過棧頂指針讀取不到,等待GC)blog
中綴表達式爲咱們人類能識別的方式,然後綴表達式是計算機進行運算的方式(即咱們上述的過程)。字符串
1)咱們使用一個stack棧結構存儲操做符,用一個List結構存儲後綴表達式結果get
2)首先讀取到數字,直接存入list中
3)當讀取到左括號"("時,直接壓棧,當讀取到運算符時,分兩種狀況討論
a.當運算符棧爲空或者棧頂操做符的優先級小於當前運算符優先級時(如+和-的優先級低於 * 和 /),直接入棧
b.當運算符不爲空時且棧頂操做符的優先級大於或等於當前運算符優先級時,循環執行出棧操做並加入list中,直到遇到優先級小於當前運算符的元素爲止。循環執行完後再將當前運算符壓棧。另外須要注意的是,只有遇到右括號時,左括號纔出棧
4) 當遇到右括號")"時,循環執行出棧操做並加入到list中,直到遇到左括號爲止。並將左括號彈出,但不加入list中
5) 表達式的值讀取完後,將操做符棧中的全部元素彈出並加入到list中
執行完上面步驟後,list中存儲的順序即爲咱們轉換後的後綴表達式的結果
下面利用上面定義的轉換規則,將表達式 1+((2+3)*4)-5 以圖解的方式描述其轉換過程
1.首先定義一個存儲操做符的棧 Stack<String> stack = new Stack<>() ,和一個存儲最終後綴表達式的列表 List<String> list = new ArrayList<>()
2.讀取表達式,首先讀取到數字 1 ,按照上述規則,直接添加至list中。此時stack和list結構以下
3.而後讀取到操做符 + ,此時stack爲空,按照上述規則,直接入棧,此時stack和list結構以下
4.接下來的兩次讀取都是左括號,按照咱們的規則,左括號直接入棧,此時stack和list結構以下
5.接着讀取到數字2,按照咱們的規則,數字直接加入list中,此時stack和list結構以下
6.接着讀取到操做符+,按照咱們的規則,此時棧不爲空且棧頂元素爲左括號,而只有遇到右括號時,左括號纔出棧,因此+運算符直接入棧,此時stack和list結構以下
7. 接着讀取到數字3,根據咱們的規則,數字直接加入list中,此時stack和list結構以下
8. 繼續向後讀取,讀到到右括號 ")",按照咱們的規則,執行stack出棧並加入list中操做,直到遇到左括號,並將左括號彈出,但不加入list中,此時stack和list結構以下
9.接着讀取到操做符 * ,按照咱們的規則,此時棧頂元素爲左括號,只需將操做符壓棧便可,此時stack和list結構以下
10.接下來讀取到數字4,按照規則直接將數字加入list中便可,此時stack和list結構以下
11.接下來讀取到右括號")",按照咱們的規則,執行stack出棧並加入list中操做,直到遇到左括號,並將左括號彈出,但不加入list中,此時stack和list結構以下
12.繼續向後讀取,此時讀取到操做符-,按照咱們的規則,當棧不爲空且當前優先級小於等於棧頂操做符優先級時,循環執行出棧並加入list操做。循環執行完再將當前操做符入棧
13.讀取最後一個元素爲數字5,按照規則,直接加入list中便可。當表達式讀取完後,依此彈出操做符棧中的全部元素,並加入list中,此時stack和list結構以下
此時list中的順序即爲咱們轉換後的後綴表達式的順序,即:1 2 3 + 4 * + 5 -
1 private static List<String> parseToSuffixExpression(List<String> expressionList) { 2 //建立一個棧用於保存操做符 3 Stack<String> opStack = new Stack<>(); 4 //建立一個list用於保存後綴表達式 5 List<String> suffixList = new ArrayList<>(); 6 for(String item : expressionList){ 7 //獲得數或操做符 8 if(isOperator(item)){ 9 //是操做符 判斷操做符棧是否爲空 10 if(opStack.isEmpty() || "(".equals(opStack.peek()) || priority(item) > priority(opStack.peek())){ 11 //爲空或者棧頂元素爲左括號或者當前操做符大於棧頂操做符直接壓棧 12 opStack.push(item); 13 }else { 14 //不然將棧中元素出棧如隊,直到遇到大於當前操做符或者遇到左括號時 15 while (!opStack.isEmpty() && !"(".equals(opStack.peek())){ 16 if(priority(item) <= priority(opStack.peek())){ 17 suffixList.add(opStack.pop()); 18 } 19 } 20 //當前操做符壓棧 21 opStack.push(item); 22 } 23 }else if(isNumber(item)){ 24 //是數字則直接入隊 25 suffixList.add(item); 26 }else if("(".equals(item)){ 27 //是左括號,壓棧 28 opStack.push(item); 29 }else if(")".equals(item)){ 30 //是右括號 ,將棧中元素彈出入隊,直到遇到左括號,左括號出棧,但不入隊 31 while (!opStack.isEmpty()){ 32 if("(".equals(opStack.peek())){ 33 opStack.pop(); 34 break; 35 }else { 36 suffixList.add(opStack.pop()); 37 } 38 } 39 }else { 40 throw new RuntimeException("有非法字符!"); 41 } 42 } 43 //循環完畢,若是操做符棧中元素不爲空,將棧中元素出棧入隊 44 while (!opStack.isEmpty()){ 45 suffixList.add(opStack.pop()); 46 } 47 return suffixList; 48 } 49 /** 50 * 判斷字符串是否爲操做符 51 * @param op 52 * @return 53 */ 54 public static boolean isOperator(String op){ 55 return op.equals("+") || op.equals("-") || op.equals("*") || op.equals("/"); 56 } 57 58 /** 59 * 判斷是否爲數字 60 * @param num 61 * @return 62 */ 63 public static boolean isNumber(String num){ 64 return num.matches("\\d+"); 65 } 66 67 /** 68 * 獲取操做符的優先級 69 * @param op 70 * @return 71 */ 72 public static int priority(String op){ 73 if(op.equals("*") || op.equals("/")){ 74 return 1; 75 }else if(op.equals("+") || op.equals("-")){ 76 return 0; 77 } 78 return -1; 79 }
這裏爲了方便操做,將原中綴表達式字符串轉換爲list結構,轉換list的代碼以下
1 /** 2 * 將表達式轉爲list 3 * @param expression 4 * @return 5 */ 6 private static List<String> expressionToList(String expression) { 7 int index = 0; 8 List<String> list = new ArrayList<>(); 9 do{ 10 char ch = expression.charAt(index); 11 if(ch < 47 || ch > 58){ 12 //是操做符,直接添加至list中 13 index ++ ; 14 list.add(ch+""); 15 }else if(ch >= 47 && ch <= 58){ 16 //是數字,判斷多位數的狀況 17 String str = ""; 18 while (index < expression.length() && expression.charAt(index) >=47 && expression.charAt(index) <= 58){ 19 str += expression.charAt(index); 20 index ++; 21 } 22 list.add(str); 23 } 24 }while (index < expression.length()); 25 return list; 26 }
注:char類型本質爲int類型,查看assic碼錶可知,0~9對應的char在 47~58之間,因此代碼依此來判斷是數字仍是操做符。另外代碼中有判斷多位數狀況,請注意
下面展現測試代碼
1 public static void main(String []args){ 2 3 String expression = "1+((2+3)*4)-5"; 4 List<String> expressionList = expressionToList(expression); 5 System.out.println("expressionList="+expressionList); 6 //將中綴表達式轉換爲後綴表達式 7 List<String> suffixList = parseToSuffixExpression(expressionList); 8 System.out.println(suffixList); 9 }
測試結果以下:
與咱們上述描述的結果相同,以上即爲中綴表達式轉換後綴表達式的過程及相關代碼。另外附上根據後綴表達式求值的代碼,感興趣的能夠參考
1 /** 2 * 根據後綴表達式list計算結果 3 * @param list 4 * @return 5 */ 6 private static int calculate(List<String> list) { 7 Stack<Integer> stack = new Stack<>(); 8 for(int i=0; i<list.size(); i++){ 9 String item = list.get(i); 10 if(item.matches("\\d+")){ 11 //是數字 12 stack.push(Integer.parseInt(item)); 13 }else { 14 //是操做符,取出棧頂兩個元素 15 int num2 = stack.pop(); 16 int num1 = stack.pop(); 17 int res = 0; 18 if(item.equals("+")){ 19 res = num1 + num2; 20 }else if(item.equals("-")){ 21 res = num1 - num2; 22 }else if(item.equals("*")){ 23 res = num1 * num2; 24 }else if(item.equals("/")){ 25 res = num1 / num2; 26 }else { 27 throw new RuntimeException("運算符錯誤!"); 28 } 29 stack.push(res); 30 } 31 } 32 return stack.pop(); 33 }
測試運算代碼以下
1 public static void main(String []args){ 2 3 String expression = "1+((2+3)*4)-5"; 4 List<String> expressionList = expressionToList(expression); 5 System.out.println("中綴表達式轉爲list結構="+expressionList); 6 //將中綴表達式轉換爲後綴表達式 7 List<String> suffixList = parseToSuffixExpression(expressionList); 8 System.out.println("對應的後綴表達式列表結構="+suffixList); 9 //根據後綴表達式計算結果 10 int calculateResult = calculate(suffixList); 11 System.out.printf(expression+"=%d\n",calculateResult); 12 }
計算結果以下
中綴表達式轉後綴表達式的難點在於轉換規則,固然這個規則是研究算法的人已經幫咱們制定好的,咱們只須要按照這個規則實現代碼便可。若是上述代碼有問題可在留言區回覆,謝謝