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

 更新时间:2024年06月17日 10:30:11   作者:编程经验分享  
这篇文章主要介绍了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));
        }
    }
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • springboot集成测试最小化依赖实践示例

    springboot集成测试最小化依赖实践示例

    这篇文章主要为大家介绍了springboot集成测试最小化依赖实践示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • spring boot输入数据校验(validation)的实现过程

    spring boot输入数据校验(validation)的实现过程

    web项目中,用户的输入总是被假定不安全不正确的,在被处理前需要做校验。本文介绍在spring boot项目中实现数据校验的过程,通过实例代码给大家介绍的非常详细,需要的朋友参考下吧
    2021-09-09
  • Java获取七牛云存储空间中图片外链

    Java获取七牛云存储空间中图片外链

    本文主要介绍了Java获取七牛云存储空间中图片外链,需要获取在七牛云中存储的所有图片,并返回外链地址,具有一定的参考价值,感兴趣的可以了解一下
    2023-10-10
  • 浅谈Java文件被执行的历程

    浅谈Java文件被执行的历程

    学习java以来,都是以语法,类库入手,最基本的也是最基础的java编译过程往往被我遗忘,先解释一下学习java第一课时,都听到过的一句话,“java是半解释语言”。什么是半解释语言。本文将介绍Java文件被执行的历程。
    2021-06-06
  • 浅谈java中静态方法的重写问题详解

    浅谈java中静态方法的重写问题详解

    本篇文章是对java中静态方法的重写问题进行了详细的分析介绍,需要的朋友参考下
    2013-06-06
  • 情人节写给女朋友Java Swing代码程序

    情人节写给女朋友Java Swing代码程序

    这篇文章主要为大家分享了情人节写给女朋友的java小程序,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,祝大家每天都是情人节
    2018-02-02
  • Android中几种图片特效的处理的实现方法

    Android中几种图片特效的处理的实现方法

    这篇文章主要介绍了 Android中几种图片特效的处理的实现方法的相关资料,这里有放大缩小图片,获得圆角图片,获得带倒影图片的几种方法,需要的朋友可以参考下
    2017-08-08
  • 解决pageHelper分页失效以及如何配置问题

    解决pageHelper分页失效以及如何配置问题

    这篇文章主要介绍了解决pageHelper分页失效以及如何配置问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • 详解Spring与Mybatis整合方法(基于IDEA中的Maven整合)

    详解Spring与Mybatis整合方法(基于IDEA中的Maven整合)

    这篇文章主要介绍了Spring与Mybatis整合方法(基于IDEA中的Maven整合),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-10-10
  • 详解Java中的hashcode

    详解Java中的hashcode

    这篇文章主要介绍了详解Java中的hashcode,文中有非常详细的代码示例,对正在学习java的小伙伴们有非常好的帮助,需要的朋友可以参考下
    2021-05-05

最新评论