資料結構與演算法——棧(四)逆波蘭計算器-字尾表示式
阿新 • • 發佈:2021-08-28
完成一個逆波蘭計算器,需求如下:
-
輸入一個 逆波蘭表示式,使用棧 Stack(JDK 自帶),計算器結果
-
支援 小括號 和 多位數
主要這裡是講解資料結構,因此簡化為只對整數計算
關於字首、中綴、字尾表示式可以去看我的這篇博文資料結構與演算法——棧(三)有關棧的三種表示式 —— 字首、中綴、字尾表示式
注意咯:知道逆波蘭表示式是什麼後,相對來說,實現還是比較容易的,難點其實是在於中綴表示式如何轉換為字尾表示式,因為這涉及到運算子的優先順序問題。在前面我們實現的時候,其實就是這個運算子優先順序問題很刺手。而字首、字尾表示式裡面表示式的優先順序,在形成這個表示式時就已經確定了。關於中綴表示式轉為字尾表示式的詳細講解請看後續部落格。
這裡為什麼使用 JDK 自帶的Stack呢,其實在實現中綴表示式的計算器時,部落格中資料結構與演算法——棧(二)【使用棧來實現綜合計算器-中綴表示式】是把計算器相關運算和方法都放在棧類中的,這只是為了方便操作資料,最終目的還是為了能讓我們理解棧這個資料結構,所以實際上,只是利用了棧的資料結構,並非是什麼都需要寫在棧中的。
實現思路:從前面字尾表示式求值過程來看,由於沒有了優先順序問題,全程只是在順序獲取元素,然後進行計算。
- 先將字尾表示式轉成一個 List(這裡的字尾表示式中的數字和運算子用空格隔開,具體看程式碼,只是為了方便解析來新增集合元素)
- 對這個 List 進行遍歷,然後利用棧進行計算
public class ReversePolishCalculator { public static void main(String[] args) { ReversePolishCalculator calculator = new ReversePolishCalculator(); // (3+4)*5-6 => 對應的字尾表示式 `3 4 + 5 * 6 -` 用空格作為分隔符 String postfixExpression = "3 4 + 5 * 6 -"; System.out.println("(3+4)*5-6 = " + calculator.cal(postfixExpression)); // (30+4)*5-6 => 對應的字尾表示式 `30 4 + 5 * 6 -` postfixExpression = "30 4 + 5 * 6 -"; System.out.println("(30+4)*5-6 = " + calculator.cal(postfixExpression)); // 4*5-8+60+8/2 => 對應的字尾表示式 `4 5 * 8 - 60 + 8 2 / +` postfixExpression = "4 5 * 8 - 60 + 8 2 / +"; System.out.println("4*5-8+60+8/2 = " + calculator.cal(postfixExpression)); // (3+4)-(3-4)*10,對應字尾表示式為:3 4 + 10 3 4 - * - postfixExpression = "3 4 + 10 3 4 - * -"; System.out.println("(3+4)-(3-4)*10 = " + calculator.cal(postfixExpression)); } /** * 計算一個字尾表示式的值 * * @param postfixExpression * @return */ public int cal(String postfixExpression) { return start(convert(postfixExpression)); } /** * 將字尾表示式轉換成 list * @param postfixExpression * @return */ private List<String> convert(String postfixExpression) { //表示式中的每個元素都用空格隔開,是為了方便;這裡重點不在於怎麼去解析出每一個元素了 return Arrays.asList(postfixExpression.split(" ")); } /** * 遍歷集合元素,並利用棧對其計算 * * @param postfixElements * @return */ private int start(List<String> postfixElements) { /* 比如:`(3+4)x5-6` 對應的字尾表示式 `3 4 + 5 x 6 -` 1. 從左到右掃描,將 3、4 壓入堆疊 2. 掃描到 `+` 運算子時 將彈出 4 和 3,計算 `3 + 4 = 7`,將 7 壓入棧 3. 將 5 入棧 4. 掃描到 `x` 運算子時 將彈出 5 和 7 ,計算 `7 x 5 = 35`,將 35 入棧 5. 將 6 入棧 6. 掃描到 `-` 運算子時 將彈出 6 和 35,計算 `35 - 6 = 29`,將 29 壓入棧 7. 掃描表示式結束,29 是表示式的值 */ Stack<Integer> stack = new Stack<>();//jdk提供的Stack for (String el : postfixElements) { // 如果是數字則入棧 if (el.matches("\\d+")) { stack.push(Integer.parseInt(el)); continue; } // 如果是運算子,則彈出兩個數 Integer num2 = stack.pop(); Integer num1 = stack.pop(); int res = cal(num1, num2, el.charAt(0)); //計算 stack.push(res);//將計算好的數壓入棧中 } return stack.pop();//當前棧中只有一個數,即最終結果 } /** * 計算 * * @param num1 次頂的數 * @param num2 棧頂的數 * @param oper 運算子 * @return */ private int cal(int num1, int num2, char oper) { switch (oper) { case '+': return num1 + num2; case '-': return num1 - num2; case '*': return num1 * num2; case '/': return num1 / num2; } throw new IllegalArgumentException("不支援的運算子:" + oper); } }
測試輸出
(3+4)*5-6 = 29
(30+4)*5-6 = 164
4*5-8+60+8/2 = 76
(3+4)-(3-4)*10 = 17