1. 程式人生 > >【Algorithm】逆波蘭表示式 Java實現

【Algorithm】逆波蘭表示式 Java實現

簡介

逆波蘭表示法(Reverse Polish notation,RPN,或逆波蘭記法),是一種是由波蘭數學家揚·武卡謝維奇1920年引入的數學表示式方式,在逆波蘭記法中,所有操作符置於運算元的後面,因此也被稱為字尾表示法。逆波蘭記法不需要括號來標識操作符的優先順序。

逆波蘭表示式可以用於表示式轉換,如數學公式轉換計算,很早之前做過一個計算器,但是解析公式 到時候感到萬分頭疼,今天看到逆波蘭表示式這個東西,不禁感慨:

竟然還有這種操作

果然,資料結構學不好是得吃大虧。

思路

對於常規的數學表示式,我們要做的是根據其優先順序計算結果(小學生都知道)。

程式設計實現的話,其實也很簡單,兩個棧即可實現,姑且把一個棧叫做 operators

,儲存運算子,另一個棧叫做output,儲存最終的表示式。

就三個要點:

  • 數字直接入output
  • 運算子要與operators棧頂比較,優先順序大則入棧,小於或等於則operators出棧後再入棧
  • operators棧頂若是(則無條件入棧

這裡寫圖片描述

下面舉的這個例子,只針對簡單 + - * / ( ),別的實現,如小數,平方等操作,原理都差不多,可以自行擴充套件。

對這個a*(b-c*d)+e式子來說,轉成波蘭表示式後應該是這樣子的:abcd*-*e+

1.讀到a,將a壓入output

這裡寫圖片描述

2.讀到*operators棧為空,直接將*壓入operators

這裡寫圖片描述

3.讀到(優先順序最高,無條件壓入operators

這裡寫圖片描述

4.讀到b,將b壓入output

這裡寫圖片描述

5.讀到-,因為有(的存在,無視比之前的*(優先順序低

這裡寫圖片描述

6.讀到c,將c壓入output

這裡寫圖片描述

7.讀到*,因為比+的優先順序高,壓入operators

這裡寫圖片描述

8.讀到d,將d壓入output

這裡寫圖片描述

9.讀到),將operators(之後的所有運算子彈出並壓入output

這裡寫圖片描述

10.讀到+,將operators所有棧頂優先順序比+的運算子彈出,最後將+壓入operators

這裡寫圖片描述

11.讀到e,將e壓入output

這裡寫圖片描述

12.表示式讀取完,將output所有運算子壓入output

這裡寫圖片描述

實現

以下程式碼是Java的實現,字串解析和逆波蘭表示式解析放在一起,但還是根據上面的思路來解決。


import java.util.Stack;

public class ReversePolishNotation {
    public static void main(String[] args) {
        //測試用例
        //String str = "1+2*3-4*5-6+7*8-9"; //123*+45*-6-78*+9-
        String str = "a*(b-c*d)+e-f/g*(h+i*j-k)"; // abcd*-*e+fg/hij*+k-*-
        //String str = "6*(5+(2+3)*8+3)"; //6523+8*+3+*
        //String str = "a+b*c+(d*e+f)*g"; //abc*+de*f+g*f

        Stack<Character> operators = new Stack<>(); //運算子
        Stack output = new Stack(); //輸出結果
        rpn(operators, output, str);
        System.out.println(output);
    }

    public static void rpn(Stack<Character> operators, Stack output, String str) {
        char[] chars = str.toCharArray();
        int pre = 0;
        boolean digital; //是否為數字(只要不是運算子,都是數字),用於擷取字串
        int len = chars.length;
        int bracket = 0; // 左括號的數量

        for (int i = 0; i < len; ) {
            pre = i;
            digital = Boolean.FALSE;
            //擷取數字
            while (i < len && !Operator.isOperator(chars[i])) {
                i++;
                digital = Boolean.TRUE;
            }

            if (digital) {
                output.push(str.substring(pre, i));
            } else {
                char o = chars[i++]; //運算子
                if (o == '(') {
                    bracket++;
                }
                if (bracket > 0) {
                    if (o == ')') {
                        while (!operators.empty()) {
                            char top = operators.pop();
                            if (top == '(') {
                                break;
                            }
                            output.push(top);
                        }
                        bracket--;
                    } else {
                        //如果棧頂為 ( ,則直接新增,不顧其優先順序
                        //如果之前有 ( ,但是 ( 不在棧頂,則需判斷其優先順序,如果優先順序比棧頂的低,則依次出棧
                        while (!operators.empty() && operators.peek() != '(' && Operator.cmp(o, operators.peek()) <= 0) {
                            output.push(operators.pop());
                        }
                        operators.push(o);
                    }
                } else {
                    while (!operators.empty() && Operator.cmp(o, operators.peek()) <= 0) {
                        output.push(operators.pop());
                    }
                    operators.push(o);
                }
            }

        }
        //遍歷結束,將運算子棧全部壓入output
        while (!operators.empty()) {
            output.push(operators.pop());
        }
    }
}

enum Operator {
    ADD('+', 1), SUBTRACT('-', 1),
    MULTIPLY('*', 2), DIVIDE('/', 2),
    LEFT_BRACKET('(', 3), RIGHT_BRACKET(')', 3); //括號優先順序最高
    char value;
    int priority;

    Operator(char value, int priority) {
        this.value = value;
        this.priority = priority;
    }

    /**
     * 比較兩個符號的優先順序
     *
     * @param c1
     * @param c2
     * @return c1的優先順序是否比c2的高,高則返回正數,等於返回0,小於返回負數
     */
    public static int cmp(char c1, char c2) {
        int p1 = 0;
        int p2 = 0;
        for (Operator o : Operator.values()) {
            if (o.value == c1) {
                p1 = o.priority;
            }
            if (o.value == c2) {
                p2 = o.priority;
            }
        }
        return p1 - p2;
    }

    /**
     * 枚舉出來的才視為運算子,用於擴充套件
     *
     * @param c
     * @return
     */
    public static boolean isOperator(char c) {
        for (Operator o : Operator.values()) {
            if (o.value == c) {
                return true;
            }
        }
        return false;
    }
}

參考

資料結構與演算法分析-Java語言描述(P67)