Java 使用逆波兰式(后缀表达式)计算表达式的值

目录

问题

如何解决

代码

操作符枚举类

表达式工具类

测试类


问题

实际项目中需要对接口的返回结果做处理,按照配置中的返回结果字段和算术运算符(+ - * /)和左右括号组合得到一个算术表达式,并计算得到值返回。

如何解决

  • 获取到接口的返回值后,便可以得到一个具体的中缀表达式
  • 将中缀表达式转化成后缀表达式(逆波兰式)
  • 计算后缀表达式的值

代码

操作符枚举类

public enum Operator {

    ADD(30, '+'),
    SUB(30, '-'),
    MUL(40, '*'),
    DIV(40, '/'),
    LEFT(0, '('),
    RIGHT(0, ')');

    static {
        map = Arrays.stream(values()).collect(toMap(ExprUtils.Operator::getSymbol, e -> e));
    }

    private final int priority;
    private final char symbol;

    private static final Map<Character, ExprUtils.Operator> map;

    Operator(Integer priority, Character symbol) {
        this.priority = priority;
        this.symbol = symbol;
    }

    public Character getSymbol() {
        return this.symbol;
    }

    //返回对应的优先级数字
    public static int getPriority(Character symbol) {
        ExprUtils.Operator operator = map.get(symbol);
        assert Objects.nonNull(operator);
        return operator.priority;
    }

    public static boolean notRightBracket(Character symbol) {
        ExprUtils.Operator operator = map.get(symbol);
        if (Objects.isNull(operator)) {
            return false;
        }
        return !operator.equals(RIGHT);
    }

    public static boolean isOperator(Character symbol) {
        ExprUtils.Operator operator = map.get(symbol);
        if (Objects.isNull(operator)) {
            return false;
        }
        switch (operator) {
            case ADD:
            case SUB:
            case MUL:
            case DIV: return true;
            default: return false;
        }
    }
}

表达式工具类

public class ExprUtils {

    /*判断中缀表达式是否合法*/
    public static boolean expressionCheck(String str) {
        Deque<Character> stack = new LinkedList<>();
        //不合法情况1:括号不匹配
        for(char ch : str.toCharArray()) {
            if(ch == '(') {
                stack.push(ch);
            }

            if(ch == ')') {
                if(stack.isEmpty()) {
                    return false;
                }
                stack.pop();
            }
        }
        if (!stack.isEmpty()) {
            return false;
        }

        //不合法情况2:操作符左右元素不合法
        for(int i = 0 ; i < str.length() ; i++) {
            //先判断 '-' 是不是负数符号
            if('-' == str.charAt(i)) {
                if( 0 == i || Operator.notRightBracket(str.charAt(i - 1))) //如果 - 在第一位或者前面有+-*/(,一定是作为负数符号而非操作符
                    i++;
            }
            if(Operator.isOperator(str.charAt(i))) {
                if( 0 == i || str.length() - 1 == i)
                    return false;
                if( Operator.isOperator(str.charAt(i - 1)) || Operator.isOperator(str.charAt(i + 1)) )
                    return false;
                if( '(' == str.charAt(i - 1) || ')' == str.charAt(i + 1) )
                    return false;
            }
        }

        //不合法情况3:左括号和右括号相邻,例:a+()和(a+b)(a+b)
        for(int i = 0 ; i < str.length() ; i++) {
            if(str.charAt(i) =='(' && i + 1 < str.length() && str.charAt(i + 1) == ')')
                return false;
            if(str.charAt(i) == ')' && i + 1 < str.length() && str.charAt(i + 1) == '(')
                return false;
        }
        return true;
    }

    /**
     * 将中缀表达式转化为后缀表达式
     *
     * @param exp 中缀表达式
     * @return 后缀表达式
     */
    public static List<String> parseExpression(String exp) {

        Deque<Character> operation = new LinkedList<>();
        List<String> target = new ArrayList<>();

        for (int i = 0; i < exp.length(); i++) {
            if(((exp.charAt(i) == '-' || exp.charAt(i) == '+') && (i == 0 || Operator.notRightBracket(exp.charAt(i - 1))))
                    || Character.isDigit(exp.charAt(i)) ) {
                // 如果是正号就不用加入,负号或者数字本身都要加入
                StringBuilder tempStr = new StringBuilder(exp.charAt(i) != '+' ? exp.substring(i, i + 1) : "");
                while (i + 1 <  exp.length() && Character.isDigit(exp.charAt(i + 1))) {
                    tempStr.append(exp.charAt(++i));
                }
                target.add(tempStr.toString());
            } else {
                if ('(' == exp.charAt(i)) {
                    operation.push(exp.charAt(i));
                } else if (')' == exp.charAt(i)) {
                    while ('(' != operation.peek()) {
                        target.add(operation.pop().toString());
                    }
                    operation.pop();
                } else {
                    while (!operation.isEmpty()
                            && Operator.getPriority(operation.peek()) >= Operator.getPriority(exp.charAt(i))) {
                        target.add(operation.pop().toString());
                    }

                    operation.push(exp.charAt(i));
                }
            }
        }

        while (!operation.isEmpty()) {
            target.add(operation.pop().toString());
        }

        return target;
    }

    /**
     * 计算后缀表达式
     *
     * @param list 后缀表达式
     * @return 计算结果
     */
    public static int calculate(List<String> list) {
        Stack<Integer> stack = new Stack<>();// 创建栈
        for (String item : list) {
            if (item.matches("^-?\\d+(\\.\\d+)?$")) { //使用正则表达式匹配多位数
                stack.push(Integer.parseInt(item));
            } else {
                int num2 = stack.pop();	// pop出两个数,并运算, 再入栈
                int num1 = stack.pop();
                int res;
                switch (item) {
                    case "+": res = num1 + num2; break;
                    case "-": res = num1 - num2; break;
                    case "*": res = num1 * num2; break;
                    case "/": res = num1 / num2; break;
                    default: throw new RuntimeException("运算符有误");
                }
                stack.push(res);
            }

        }

        return stack.pop();
    }
}

测试类

class ExprUtilsTest {

    @Test
    void calculate() {
        String infixExpression = "-6+((-2)+(2+4))";
        if (!ExprUtils.expressionCheck(infixExpression)) {
            System.out.println("表达式不合法");
        } else {
            System.out.println("中缀表达式为:" + infixExpression);
            List<String> calculateExpression = ExprUtils.parseExpression(infixExpression);
            System.out.println("后缀表达式为:" + calculateExpression);
            System.out.printf("Answer=%d", ExprUtils.calculate(calculateExpression));
        }
    }
}

  • 20
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值