如何为含+、-、*、/、%的计算器实现运算符优先级?
解决计算器运算符优先级问题
你的问题很典型——当前代码是从左到右顺序计算,完全没考虑乘除(*、/、%)比加减(+、-)优先级高的规则,所以计算3+3-4*8+5/6+5时会得到错误结果。
我给你两种可行的实现思路,先讲最直观的分步处理法,再讲更通用的栈方法(适合以后扩展更多运算符)。
方法一:分步处理(先乘除后加减)
核心思路是拆分表达式后分两次遍历:
- 第一次专门处理所有乘除、取模运算,把计算结果替换掉对应的数字和运算符,生成只含加减和数字的新列表
- 第二次遍历新列表,处理所有加减运算得到最终结果
修改后的代码如下:
import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Calculator { public String calculateExpression(String expression) { // 拆分表达式为数字和运算符(包含%) String[] tokens = expression.split("(?=[*/+-])|(?<=[*/+-])"); List<String> tokenList = new ArrayList<>(Arrays.asList(tokens)); // 第一步:处理乘除和取模运算 int i = 0; while (i < tokenList.size()) { String op = tokenList.get(i); if (op.equals("*") || op.equals("/") || op.equals("%")) { // 获取运算符前后的数字 double left = Double.parseDouble(tokenList.get(i - 1)); double right = Double.parseDouble(tokenList.get(i + 1)); double result = 0.0; switch (op) { case "*": result = Multiplication(left, right); break; case "/": result = Division(left, right); break; case "%": result = Modulus(left, right); break; } // 替换:移除左数、运算符、右数,插入计算结果 tokenList.remove(i - 1); tokenList.remove(i - 1); // 原运算符现在在i-1位置 tokenList.remove(i - 1); // 原右数现在在i-1位置 tokenList.add(i - 1, String.valueOf(result)); // 移除后列表长度变化,不需要i++,继续检查当前位置 } else { i++; } } // 第二步:处理加减运算 double dres = Double.parseDouble(tokenList.get(0)); for (i = 1; i < tokenList.size(); i += 2) { String op = tokenList.get(i); double val = Double.parseDouble(tokenList.get(i + 1)); switch (op) { case "+": dres = Addition(dres, val); break; case "-": dres = Subtraction(dres, val); break; } } return Double.toString(dres); } // 保留你原来的运算方法 public double Addition(double d1, double d2) { return d1 + d2; } public double Subtraction(double d1, double d2) { return d1 - d2; } public double Multiplication(double d1, double d2) { return d1 * d2; } public double Division(double d1, double d2) { return d1 / d2; } public double Modulus(double d1, double d2) { return d1 % d2; } }
这个方法逻辑直观,容易理解和调试,完全适配你当前的需求。
方法二:后缀表达式(逆波兰表达式)法
如果以后需要支持括号或者更多复杂运算符(比如平方、开方),后缀表达式会更通用。核心步骤是:
- 将中缀表达式(比如
3+4*2)转换成后缀表达式(3 4 2 * +) - 遍历后缀表达式,用栈计算最终结果
简化版实现代码如下:
import java.util.HashMap; import java.util.Map; import java.util.Stack; public class Calculator { public String calculateExpression(String expression) { String[] tokens = expression.split("(?=[*/+-])|(?<=[*/+-])"); Stack<Double> numStack = new Stack<>(); Stack<String> opStack = new Stack<>(); // 定义运算符优先级:乘除模优先级高于加减 Map<String, Integer> priority = new HashMap<>(); priority.put("+", 1); priority.put("-", 1); priority.put("*", 2); priority.put("/", 2); priority.put("%", 2); for (String token : tokens) { if (token.matches("-?\\d+(\\.\\d+)?")) { // 匹配数字(支持负数和小数) numStack.push(Double.parseDouble(token)); } else { // 处理运算符 // 栈顶运算符优先级更高时,先计算再压栈 while (!opStack.isEmpty() && priority.get(opStack.peek()) >= priority.get(token)) { double right = numStack.pop(); double left = numStack.pop(); String op = opStack.pop(); numStack.push(calculate(left, right, op)); } opStack.push(token); } } // 处理栈中剩余的运算符 while (!opStack.isEmpty()) { double right = numStack.pop(); double left = numStack.pop(); String op = opStack.pop(); numStack.push(calculate(left, right, op)); } return Double.toString(numStack.pop()); } private double calculate(double left, double right, String op) { switch (op) { case "+": return left + right; case "-": return left - right; case "*": return left * right; case "/": return left / right; case "%": return left % right; default: throw new IllegalArgumentException("Invalid operator: " + op); } } // 原运算方法可保留,也可直接使用上面的calculate方法 }
测试你的示例表达式
对于3+3-4*8+5/6+5,正确结果应为≈-20.1667,用上面任意一种代码都能得到这个正确结果。
内容的提问来源于stack exchange,提问作者marcus.w
相关产品推荐
相关产品推荐

