java 栈的应用 - 实现带括号的加减乘除计算器

上篇说了怎么实现一个不带括号的表达式求值方法,由于现在的带了括号了,之前的方法就不适用了。

上篇对应地址:java 栈的应用 - 实现简单的加减乘除的计算器

这里主要是分两步:

  1. 将给定的输入转成后缀表达式;
  2. 根据后缀表达式进行运算;

先看转后缀表达式的代码:

import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 中缀表达式转后缀表达式
 */
public class ToPostfixExpression {

    private static final int CAPACITY = Integer.MAX_VALUE;
    private final Stack<Operator> ops = new Stack<>();
    private final Stack<String> results = new Stack<>();
    private final Pattern numPattern = Pattern.compile("\\d*\\.?\\d*");
    private final Pattern opPattern = Pattern.compile("[+\\-*/()]");

   
    public String apply(String input) {
        // 1 + ((2+3) * 4 ) - 5
        input = input.replaceAll(" ", "");
        Matcher numMatcher = numPattern.matcher(input);
        Matcher opm = opPattern.matcher(input);

        for (int pos = 0; pos < input.length(); ) {
            boolean found = numMatcher.find(pos);
            if (found) {
                int start = numMatcher.start();
                int end = numMatcher.end();
                pos = end;
                if (start != end) {
                    String sub = input.substring(start, end);
                    // System.out.printf("number: (%d, %d)=%s\n", start, end, sub);
                    results.push(sub);
                }
            }
            found = opm.find(pos);
            if (found) {
                int start = opm.start();
                int end = opm.end();
                pos = end;
                if (start != end) {
                    try {
                        String sub = input.substring(start, end);
                        // System.out.printf("op: (%d, %d)=%s\n", start, end, sub);
                        Operator action = Operator.convert(sub);
                        if (action == Operator.LEFT) {
                            ops.push(action);
                        } else if (action == Operator.RIGHT) {
                            // System.err.println("before pop------ " + ops);
                            Operator ele;
                            do {
                                ele = ops.pop();
                                if (ele != Operator.LEFT) {
                                    results.push(ele.getOperate());
                                }
                            } while (ele != Operator.LEFT);
                            // System.err.println("pop------ " + ops);
                        } else {
                            boolean actionPushed = false;
                            while (!actionPushed) {
                                if (ops.isEmpty()) {
                                    ops.push(action);
                                    actionPushed = true;
                                } else {
                                    Operator top = ops.pop();
                                    if (Operator.LEFT == top
                                            || action.getPriority() > top.getPriority()) {
                                        ops.push(top);
                                        ops.push(action);
                                        actionPushed = true;
                                    } else {
                                        results.push(top.getOperate());
                                    }
                                }
                                // System.out.println("in loop ------ " + ops);
                            }
                        }
                    } catch (Operator.IllegalOperationException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
        // after loop, pop all ele from ops to results;
        while (!ops.isEmpty()) {
            Operator op = ops.pop();
            results.push(op.getOperate());
        }
        System.out.println("after loop, results: " + results);
        // revert the results, then the final stack is the result.
        StringBuilder sb = new StringBuilder();
        while (!results.isEmpty()) {
            String ele = results.pop();
            sb.insert(0, ele.concat(", "));
        }
        int last = sb.lastIndexOf(", ");
        if (last != -1) {
            sb.delete(last, last + 2);
        }
        return sb.toString().trim();
    }

    public static void main(String[] args) {
        // String input = "123 + ((21+3.7) * 4 ) - 5";
        String input = "(3 +4.23.4 ) * 5 -6...";
        ToPostfixExpression expression = new ToPostfixExpression();
        String apply = expression.apply(input);
        System.out.printf("transform(%s) -> [%s]\n", input, apply);
    }
}

输出如下:

transform((3 +4.23.4 ) * 5 -6) -> [3, 4.23, +, 5, *, 6, -]

然后是计算后缀表达式:


import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Stack;

public class Calculator {

    private static final String NUM_REGEX = "\\d*\\.?\\d*";
    private static final String OP_REGEX = "[+\\-*/]";
    private final Stack<BigDecimal> numbers = new Stack<>();
    private final ToPostfixExpression transformer = new ToPostfixExpression();

    
    public String apply(String input) {
        String postfixExpression = transformer.apply(input).replaceAll(" ", "");
        System.out.println("postfixExpression: " + postfixExpression);
        return calcPostfixExpression(postfixExpression);
    }

    private String calcPostfixExpression(String postfixExpression) {
        String[] elements = postfixExpression.split(",");
        System.out.println("split:::: " + Arrays.toString(elements));
        for (String ele : elements) {
            if (ele.matches(NUM_REGEX)) {
                double value = Double.parseDouble(ele);
                BigDecimal bv = BigDecimal.valueOf(value);
                numbers.push(bv);
            } else if (ele.matches(OP_REGEX)) {
                try {
                    Operator op = Operator.convert(ele);
                    BigDecimal top = numbers.pop();
                    BigDecimal prev = numbers.pop();
                    BigDecimal result = Operator.calculate(op, prev, top);
                    System.out.printf("%s %s %s = %s\n", prev, op, top, result);
                    numbers.push(result);
                } catch (Operator.IllegalOperationException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return numbers.pop().stripTrailingZeros().toPlainString();
    }

    public static void main(String[] args) {

        Calculator cal = new Calculator();
//        String input = "(3 +4 ) * 5 -6";
        String input = "123 + ((21+3.7) * 4 ) - 5"; // error
//        String input = "1 + ((2+3) * 4 ) - 5";
        String result = cal.apply(input);
        System.out.printf("compute(%s) -> [%s]\n", input, result);
    }
}

输出如下:

compute(123 + ((21+3.7) * 4 ) - 5) -> [216.8]

里面用到的 Operator 是自定义的操作符封装类,代码如下:

import java.math.BigDecimal;
import java.math.RoundingMode;

public enum Operator {

    LEFT("(", 0),

    RIGHT(")", 0),
    ADD("+", 1),
    REDUCE("-", 1),
    MULTI("*", 2),
    DIVIDE("/", 2);

    private final String operate;
    private final int priority;

    Operator(String operate, int priority) {
        this.operate = operate;
        this.priority = priority;
    }

    public String getOperate() {
        return operate;
    }

    public int getPriority() {
        return priority;
    }

    public static Operator convert(String operate)
            throws IllegalOperationException {
        return switch (operate) {
            case "+" -> ADD;
            case "-" -> REDUCE;
            case "*" -> MULTI;
            case "/" -> DIVIDE;
            case "(" -> LEFT;
            case ")" -> RIGHT;
            default -> throw new IllegalOperationException("illegal operator: " + operate);
        };
    }

    public static int calculate(Operator op, int prev, int cur)
            throws IllegalOperationException {
        return switch (op) {
            case ADD -> prev + cur;
            case REDUCE -> prev - cur;
            case MULTI -> prev * cur;
            case DIVIDE -> prev / cur;
            default -> throw new IllegalOperationException("illegal operator.");
        };
    }

    public static BigDecimal calculate(Operator op, BigDecimal prev, BigDecimal cur)
            throws IllegalOperationException {
        return switch (op) {
            case ADD -> prev.add(cur);
            case REDUCE -> prev.subtract(cur);
            case MULTI -> prev.multiply(cur);
            case DIVIDE -> prev.divide(cur, RoundingMode.HALF_UP);
            default -> throw new IllegalOperationException("illegal operator.");
        };
    }

    public static class IllegalOperationException extends Exception {

        public IllegalOperationException(String message) {
            super(message);
        }

    }
}

以上就实现了一个可以计算 带括号的加减乘除的算术表达式(比如"123 + ((21+3.7) * 4 ) - 5")的值了。

大体上使用了栈去进行中间值的存储,使用了正则去分离表达式中的数字与操作符。(这里限定操作符只能是+-*/()这几个)

然后,并没有去验证输入的合法性。因为这个太复杂了感觉…

注意,这里的转后缀及计算后缀的算法是波兰数学家扬·武卡谢维奇提出的。
可以看对应的百科解释:逆波兰式

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值