1. 程式人生 > 其它 >資料結構與演算法——棧(四)逆波蘭計算器-字尾表示式

資料結構與演算法——棧(四)逆波蘭計算器-字尾表示式

完成一個逆波蘭計算器,需求如下:

  1. 輸入一個 逆波蘭表示式,使用棧 Stack(JDK 自帶),計算器結果

  2. 支援 小括號多位數

    主要這裡是講解資料結構,因此簡化為只對整數計算

    關於字首、中綴、字尾表示式可以去看我的這篇博文資料結構與演算法——棧(三)有關棧的三種表示式 —— 字首、中綴、字尾表示式

注意咯:知道逆波蘭表示式是什麼後,相對來說,實現還是比較容易的,難點其實是在於中綴表示式如何轉換為字尾表示式,因為這涉及到運算子的優先順序問題。在前面我們實現的時候,其實就是這個運算子優先順序問題很刺手。而字首、字尾表示式裡面表示式的優先順序,在形成這個表示式時就已經確定了。關於中綴表示式轉為字尾表示式的詳細講解請看後續部落格。

這裡為什麼使用 JDK 自帶的Stack呢,其實在實現中綴表示式的計算器時,部落格中資料結構與演算法——棧(二)【使用棧來實現綜合計算器-中綴表示式】是把計算器相關運算和方法都放在棧類中的,這只是為了方便操作資料,最終目的還是為了能讓我們理解棧這個資料結構,所以實際上,只是利用了棧的資料結構,並非是什麼都需要寫在棧中的。

實現思路:從前面字尾表示式求值過程來看,由於沒有了優先順序問題,全程只是在順序獲取元素,然後進行計算。

  1. 先將字尾表示式轉成一個 List(這裡的字尾表示式中的數字和運算子用空格隔開,具體看程式碼,只是為了方便解析來新增集合元素)
  2. 對這個 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