【題解】表達式求值的兩種解法(Java版) - 二叉樹|雙棧結構

表達式求值

求一個非負整數四則混合運算且含嵌套括號表達式的值。如:

# 輸入:
1+2*(6/2)-4
# 輸出:
3.0

數據保證:

  1. 保證表達式合法(含除數不爲0)。
  2. 保證運算數是非負整數。

雙棧版

維護兩個棧: 符號棧,數字棧,遍歷輸入串過程中計算

  1. 數字直接入棧
  2. 符號入棧
    a. 符號棧爲空
    b. 當前符號優先於棧頂符號
    c. 棧頂爲’(’
  3. 符號出棧計算: 棧頂符號非’(’ 且 優先級更高.
class ExpStack {
    private static final String LEVEL_OPTS = ")+-*/(";

    public double solve(String input) {
        Stack<Character> optStack = new Stack<>();
        Stack<Double> numStack = new Stack<>();
        int i = 0;
        while (i < input.length()) {
            if (Character.isDigit(input.charAt(i))) { // 如果是數字直接入棧
                int t = 0;
                for (; i < input.length() && Character.isDigit(input.charAt(i)); i++) {
                    t = t * 10 + input.charAt(i) - '0';
                }
                numStack.push((double) t);
            } else {// 如果是操作符
                //棧爲空 或 當前優先於棧頂: 入棧
                if (optStack.isEmpty() || compPriority(input.charAt(i), optStack.peek()) > 0) {
                    optStack.push(input.charAt(i++));
                } else if (optStack.peek() == '(') { // 棧頂爲左括號 '('
                    if (input.charAt(i) == ')') { // 括號完成: 彈出'('
                        optStack.pop();
                    } else { // 括號開始 : 入棧
                        optStack.push(input.charAt(i));
                    }
                    i++;
                } else { // 棧頂優先級更高且非 '(' : 運算
                    double top = numStack.pop();
                    numStack.push(calc(numStack.pop(), optStack.pop(), top));
                }
            }
        }
        while (!optStack.isEmpty()) {
            double top = numStack.pop();
            numStack.push(calc(numStack.pop(), optStack.pop(), top));
        }
        return numStack.pop();
    }

    private int compPriority(char c1, char c2) {
        return LEVEL_OPTS.indexOf(c1) - LEVEL_OPTS.indexOf(c2);
    }

    private double calc(double x, char o, double y) {
        switch (o) {
            case '+':
                return x + y;
            case '-':
                return x - y;
            case '*':
                return x * y;
            case '/':
                return x / y;
        }
        return 0;
    }
}

二叉樹版

構建二叉樹: 非葉子節點爲符號,葉子節點爲數字. 最終後序搜索計算
二分點: 表達式中最後一個計算的運算符

  1. 排除括號後
  2. 優先取 + | -
  3. 再考慮 * | /
class ExpTree {
    private String mInput;
    private java.util.LinkedList<Node> mTree;

    public double solve(String input) {
        mInput = input;
        mTree = new java.util.LinkedList<>();

        buildTree(0, mInput.length());

        return dfs(mTree.size() - 1);
    }


    private int buildTree(int li, int ri) {

        try { // 先嚐試吧表達式解析爲葉子節點(純運算數)
            int n = Integer.parseInt(mInput.substring(li, ri));
            Node node = new Node(n, -1, -1);
            mTree.addLast(node);
            return mTree.size() - 1;
        } catch (Exception ignore) {
        }

        // 找到最外層的運算符(最後一個計算的運算符,優先級最低的符號)
        int opt, as = -1, md = -1, bracket = 0;
        for (int i = li; i < ri; i++) {
            switch (mInput.charAt(i)) {
                case '(':
                    bracket++;
                    break;
                case ')':
                    bracket--;
                    break;
                case '+':
                case '-':
                    if (bracket == 0) {
                        as = i;
                    }
                    break;
                case '*':
                case '/':
                    if (bracket == 0) {
                        md = i;
                    }
                    break;
            }
        }
        opt = as < 0 ? md : as;
        if (opt < 0) { // 發現這是一個被括號包裹的表達式(去掉括號重新構造)
            return buildTree(li + 1, ri - 1);
        }
        // 依次構造左右子樹
        Node node = new Node(mInput.charAt(opt), buildTree(li, opt), buildTree(opt + 1, ri));
        mTree.addLast(node);
        return mTree.size() - 1;
    }

    private double dfs(int i) { // 後序遍歷求解
        if (mTree.get(i).lch == -1 && mTree.get(i).rch == -1) {
            return mTree.get(i).num;
        }
        switch (mTree.get(i).opt) {
            case '+':
                return dfs(mTree.get(i).lch) + dfs(mTree.get(i).rch);
            case '-':
                return dfs(mTree.get(i).lch) - dfs(mTree.get(i).rch);
            case '*':
                return dfs(mTree.get(i).lch) * dfs(mTree.get(i).rch);
            case '/':
                return dfs(mTree.get(i).lch) / dfs(mTree.get(i).rch);
        }
        return 0;
    }

    private static class Node {
        double num;
        char opt;
        int lch, rch;

        Node(double n, int l, int r) {
            num = n;
            initChild(l, r);
        }

        Node(char o, int l, int r) {
            opt = o;
            initChild(l, r);
        }

        private void initChild(int l, int r) {
            lch = l;
            rch = r;
        }
    }
}

測試驅動函數

public class Main {
    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        String input = cin.nextLine();

        double t = new ExpTree().solve(input);
        T.d(t);
        double s = new ExpStack().solve(input);
        T.d(s);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章