Java如何使用逆波兰式(后缀表达式)计算表达式的值
作者:编程经验分享
这篇文章主要介绍了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)); } } }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。