【資料結構與演算法】中綴表示式轉字尾表示式以及字尾表示式的計算

gonghr發表於2021-10-04

中綴表示式轉字尾表示式

方式一

步驟

1️⃣ 如果遇到運算元,我們就直接將其輸出。

2️⃣ 如果遇到操作符,則我們將其放入到棧中,遇到左括號時我們也將其放入棧中。

3️⃣ 如果遇到一個右括號,則將棧元素彈出,將彈出的操作符輸出直到遇到左括號為止。注意,左括號只彈出並不輸出。

4️⃣ 如果遇到任何其他的操作符,如(“+”, “*”,“(”)等,從棧中彈出元素直到遇到發現更低優先順序的元素(或者棧為空)(或發現最近的左括號)為止。彈出完這些元素後,才將遇到的操作符壓入到棧中。有一點需要注意,只有在遇到" ) "的情況下我們才彈出" ( ",其他情況我們都不會彈出" ( "。

5️⃣ 如果我們讀到了輸入的末尾,則將棧中所有元素依次彈出。

程式碼實現

package demo03;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;

public class InfixToPostfixExpression {

    private char[] expression;

    private Deque<Character> stack;

    private ArrayList<Character> list;  // 儲存答案

    public InfixToPostfixExpression(char[] ex) {
        this.expression = ex;
        stack = new ArrayDeque<>();
        list = new ArrayList<>();
    }

    public ArrayList process() {
        for (char c : expression) {
            if (isOperand(c)) {            //如果是運算元,入集合
                list.add(c);
            }
            else if (isBracket(c)) {       // 如果是括號
                if (isLeftBracket(c)) {     // 如果是左括號,入棧
                    stack.addLast(c);

                } else {                    // 如果是右括號
                    while (!stack.isEmpty() && stack.peekLast() != '(') {  // 一直彈出棧中元素知道遇到左括號(左括號也要但還沒彈出)
                        list.add(stack.removeLast());
                    }
                    if (!stack.isEmpty()) stack.removeLast();  //移除棧中匹配的左括號

                }
            } else if (isOperator(c)) {   // 如果 c 是操作符
                if (stack.isEmpty()) {    // 如果棧空,則入棧
                    stack.addLast(c);
                }
                else {
                    // 計算優先值(0:相等,-1:c優先值低於棧頂,1:c優先值大於棧頂)
                    int priority = priority(c) - priority(stack.peekLast());
                    if (priority < 0) {     //遇到優先度低的,一直彈出來(直到遇到左括號)
                        while (!stack.isEmpty() && !isLeftBracket(stack.peekLast())) {
                            list.add(stack.removeLast());
                        }
                    }
                    stack.addLast(c);  // 最後 c 入棧
                }
            }
        }
        while (!stack.isEmpty()) {   // 棧中剩餘元素都彈出併入集合
            list.add(stack.removeLast());
        }
        return list;
    }

    // 判斷是否是運算元
    private boolean isOperand(char c) {
        return !(isOperator(c) || isBracket(c));

    }

    // 計算優先值
    private int priority(char c) {
        if (c == '+' || c == '-') return 0;
        else return 1;   //if (c == '*' || c == '/')
    }

    // 判斷是否是左括號
    private boolean isLeftBracket(char c) {
        return c == '(';
    }

    // 判斷是否是右括號
    private boolean isRightBracket(char c) {
        return c == ')';
    }

    // 判斷是否是操作符
    public boolean isOperator(char c) {
        return c == '+' || c == '-' || c == '*' || c == '/';
    }

    // 判斷是否是括號
    public boolean isBracket(char c) {
        return c == '(' || c == ')';
    }

}

測試

    public static void main(String[] args) {
        String ex = "a + b * c + (d * e + f)*g";
        ex = ex.trim().replace(" ", "");
        char[] e = ex.toCharArray();
        InfixToPostfixExpression expression = new InfixToPostfixExpression(e);
        ArrayList process = expression.process();
        System.out.println(process.toString());
        // 輸出:[a, b, c, *, +, d, e, *, f, +, g, *, +]
    }

方式二

1️⃣ 先按照運算子的優先順序對中綴表示式加括號,變成((a + (b * c)) + (((d * e) + f) * g ))

2️⃣ 將運算子移到括號的後面,變成((a(bc) * )+(((de) * f) + g) * ) +

3️⃣ 去掉括號,得到 abc *+ de * f + g *+

字尾表示式的計算

字尾表示式也叫逆波蘭表示式。

逆波蘭表示式嚴格遵循「從左到右」的運算。計算逆波蘭表示式的值時,使用一個棧儲存運算元,從左到右遍歷逆波蘭表示式,進行如下操作:

如果遇到運算元,則將運算元入棧;

如果遇到運算子,則將兩個運算元出棧,其中先出棧的是右運算元,後出棧的是左運算元,使用運算子對兩個運算元進行運算,將運算得到的新運算元入棧。

整個逆波蘭表示式遍歷完畢之後,棧內只有一個元素,該元素即為逆波蘭表示式的值。

程式碼

class Solution {
    public int evalRPN(String[] tokens) {
        Deque<Integer> stack = new LinkedList<>();
        int n = tokens.length;
        for(int i = 0;i<n;i++){
            
            if(isNum(tokens[i])){
                stack.push(Integer.parseInt(tokens[i]));
            }else{
                int b = stack.pop();
                int a = stack.pop();
                switch(tokens[i]){
                    case "+":{
                        stack.push(a+b);
                        break;
                    }
                    case "-":{
                        stack.push(a-b);
                        break;
                    }
                    case "*":{
                        stack.push(a*b);
                        break;
                    }
                    case "/":{
                        stack.push(a/b);
                        break;
                    }
                }
            }
        }
        return stack.pop();
    }
    public boolean isNum(String s){
        return !(("+".equals(s))||("-".equals(s))||("*".equals(s))||("/".equals(s)));
    }
}
輸入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
輸出:22
解釋:
該算式轉化為常見的中綴算術表示式為:
  ((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

相關文章