算法的实现思路:将算术表达式转换为逆波兰表达式,然后根据逆波兰表达式的法则计算结果
1、因为我们支持小数,所以需要小数的包装类。当然,Java已经有包装类了,我们用Double类
2、自定义一个操作符类,也就是+ - * / ( ),可以认为是以上操作符的包装类
/**
* 逆波兰表达式的操作符及其工具的包装类
* @Author: LiYang
* @Date: 2019/9/7 23:59
*/
public class Operator {
//操作符名称,比如:+-*/()
public String symbol;
/**
* 构造方法,传入符号入参
* @param symbol
*/
public Operator(String symbol){
this.symbol = symbol;
}
/**
* 判断表达式是否是以操作符开头
* @param expression
* @return
*/
public static boolean isStartWithOperator(String expression){
return expression.startsWith("+") ||
expression.startsWith("-") ||
expression.startsWith("*") ||
expression.startsWith("/") ||
expression.startsWith("(") ||
expression.startsWith(")");
}
/**
* 返回该符号的优先级
* @return
*/
public int getPriority(){
switch (symbol){
case "+": return 1;
case "-": return 1;
case "*": return 2;
case "/": return 2;
case "(": return 3;
case ")": return 3;
}
throw new IllegalArgumentException("操作符应该为以下之一:+-*/()");
}
/**
* 将输入值转化为操作符类
* @param symbol
* @return
*/
public Operator parseOperator(String symbol){
switch (symbol){
case "+": return new Operator("+");
case "-": return new Operator("-");
case "*": return new Operator("*");
case "/": return new Operator("/");
case "(": return new Operator("(");
case ")": return new Operator(")");
}
throw new IllegalArgumentException("操作符应该为以下之一:+-*/()");
}
}
3、利用逆波兰表达式,计算出算术表达式的结果(大家可以自行复制代码,然后Debug模式进行研究)
3.1、最后程序所用测试用例:“3.75 + 7.84 * 3.62 + (9.93 / 2.47 - 3.27) * 7.96 + 4.32 / (3.68 - 1.03) + 0.17”
3.2、测试用例逆波兰表达式:“3.75 7.84 3.62 * + 9.93 2.47 / 3.27 - 7.96 * + 4.32 3.68 1.03 - / + 0.17 +”
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* 逆波兰表达式工具类
* @Author: LiYang
* @Date: 2019/9/8 0:07
*/
public class ReversePolishExpression {
/**
* 将算术表达式解析为类集合,操作符是Operator,操作数是Double
* @param expression 算术表达式字符串
* @return 算术表达式的解析类集合
*/
public static List parseExpression(String expression){
//先去除所有空格(如果有)
expression = expression.replaceAll(" ", "");
//最终的算术表达式解析List
List expressionList = new ArrayList();
//临时字符串片段
String fragment = "";
//循环遍历算术表达式,并逐一解析
for (int i = 0; i < expression.length(); i++) {
//如果当前字符不是操作符
if (!Operator.isStartWithOperator(expression.substring(i))){
fragment = fragment + String.valueOf(expression.charAt(i));
//如果当前字符是操作符
} else {
//如果不是空字符串
if (!"".equals(fragment)){
//先将之前的数字储存
Double number = Double.parseDouble(fragment);
//装入集合
expressionList.add(number);
//清空fragment
fragment = "";
}
//将操作符装入集合
expressionList.add(new Operator(String.valueOf(expression.charAt(i))));
}
}
//加入最后的数字
if (!"".equals(fragment)){
expressionList.add(Double.parseDouble(fragment));
}
//返回解析好的结果
return expressionList;
}
/**
* 将解析的类的集合,转化为字符串,也就是上面方法的逆操作
* @param expressionList 算术表达式的解析类集合
* @return 算术表达式字符串
*/
public static String stringifyExpression(List expressionList){
//声明一个StringBuffer,用于拼接字符串
StringBuffer expression = new StringBuffer();
//遍历添加表达式内容
for (Object item : expressionList){
//添加操作符
if (item instanceof Operator){
expression.append(" ").append(((Operator) item).symbol);
}
//添加操作数
if (item instanceof Double){
expression.append(" ").append(((Double) item).doubleValue());
}
}
//返回算术表达式字符串
return expression.toString().substring(1);
}
/**
* 将算术表达式集合类,转化为逆波兰表达式集合类
* @param infixExpressionList 算术表达式集合类
* @return 逆波兰表达式集合类
*/
public static List convertReversePolandExpression(List infixExpressionList){
//声明一个空栈,备用
Stack stack = new Stack();
//输出的逆波兰表达式集合类
List reversePolandExpression = new ArrayList();
//重要:遍历算术表达式(此for循环里面的内容,是该算法的精髓)
for (Object item : infixExpressionList){
//如果是操作数
if (item instanceof Double) {
//则直接放到逆波兰表达式中
reversePolandExpression.add(item);
//如果是操作符
} else {
//获取当前操作符
Operator currentOperator = (Operator)item;
//获取操作符字符串
String symbol = currentOperator.symbol;
//1、如果是右括号
if (")".equals(symbol)){
//弹出栈顶元素,直到遇见左括号
while (!stack.empty() && !stack.peek().symbol.equals("(")){
//弹出的符号,加入到逆波兰表达式
reversePolandExpression.add(stack.pop());
}
//遇到左括号,则直接弹出,不记录
if (!stack.empty() && stack.peek().symbol.equals("(")){
//直接弹出
stack.pop();
}
//2、如果栈不为空,且栈顶元素的优先级不低于当前操作符
} else if (!stack.empty() && stack.peek().getPriority() >= currentOperator.getPriority()){
//栈不为空的前提下弹出栈顶元素,直到遇到左括号或者栈顶元素优先级小于当前元素
while (!stack.empty() && !stack.peek().symbol.equals("(") &&
stack.peek().getPriority() >= currentOperator.getPriority()){
//将弹出的操作符加入逆波兰表达式
reversePolandExpression.add(stack.pop());
}
//最后,自己入栈
stack.push(currentOperator);
//3、其他情况,则直接操作符入栈
} else {
//直接入栈
stack.push((Operator) item);
}
}
}
//最后,如果栈内还有操作符,则全部出栈
while (!stack.empty()){
//出栈后全部加入逆波兰表达式
reversePolandExpression.add(stack.pop());
}
//最后,返回逆波兰表达式集合类
return reversePolandExpression;
}
/**
* 计算表达式结果
* @param firstNumber 第一个操作数
* @param symbol 计算符号
* @param secondNumber 第二个操作数
* @return 计算结果
*/
public static double calculator(double firstNumber, String symbol, double secondNumber){
// 除数是0的判断
if ("/".equals(symbol) && secondNumber == 0){
throw new ArithmeticException("除数不能是0");
}
//根据不同的操作类型,返回不同的计算结果
switch (symbol){
case "+": return firstNumber + secondNumber;
case "-": return firstNumber - secondNumber;
case "*": return firstNumber * secondNumber;
case "/": return firstNumber / secondNumber;
}
//如果能到这里,则表示操作符参数有问题
throw new IllegalArgumentException("操作符只支持:+ - * /");
}
/**
* 根据逆波兰表达式,计算表达式结果
* @param expressionList 逆波兰表达式
* @return 表达式计算结果
*/
public static Double calculateReversePolandExpression(List expressionList){
//声明一个空栈,备用
Stack stack = new Stack<>();
//遍历逆波兰表达式集合
for (Object item : expressionList){
//如果是数字
if (item instanceof Double){
//直接入栈
stack.push((Double) item);
//如果是操作符
} else {
//获得操作符
String symbol = ((Operator)item).symbol;
//弹出第二个操作数
double secondNumber = stack.pop();
//弹出第一个操作数
double firstNumber = stack.pop();
//计算出结果
Double resultNumber = calculator(firstNumber, symbol, secondNumber);
//将计算结果,入栈
stack.push(resultNumber);
}
}
//最后栈里还会剩下一个数,也就是计算结果
return stack.peek();
}
/**
* 四则运算工具,可以计算四则运算,支持小数及以下运算符:+ - * / ( )
* 入参示例:"3.75 + 7.84 * 3.62 + (9.93 / 2.47 - 3.27) * 7.96 + 4.32 / (3.68 - 1.03) + 0.17"
* @param arithmeticExpression 算术表达式
* @return
*/
public static double fourFundamentalRulesCalculator(String arithmeticExpression){
//先将算术表达式解析为相应集合类
List arithmeticExpressionList = parseExpression(arithmeticExpression);
//将算术表达式集合类,转换为逆波兰表达式集合类
List reversePolandExpressionList = convertReversePolandExpression(arithmeticExpressionList);
//可以在这里输出逆波兰表达式
//System.out.println(stringifyExpression(reversePolandExpressionList));
//将上述逆波兰表达式集合类进行计算,得出结果
double result = calculateReversePolandExpression(reversePolandExpressionList);
//返回计算结果
return result;
}
/**
* 四则运算工具的功能性测试
* @param args
*/
public static void main(String[] args) {
//算术表达式字符串
String arithmeticExpression = "3.75 + 7.84 * 3.62 + (9.93 / 2.47 - 3.27) * 7.96 + 4.32 / (3.68 - 1.03) + 0.17";
//通过四则运算工具,计算出结果
double fourFundamentalRulesCalculatedResult = fourFundamentalRulesCalculator(arithmeticExpression);
//打印计算结果
System.out.println("四则运算工具计算结果:\t" + fourFundamentalRulesCalculatedResult);
//打印Java程序计算的结果
System.out.println("Java程序计算的结果:\t" + ( 3.75 + 7.84 * 3.62 + (9.93 / 2.47 - 3.27) * 7.96 + 4.32 / (3.68 - 1.03) + 0.17 ));
}
}
4、最后,运行测试用例,测试通过,控制台输出:
四则运算工具计算结果: 39.90292228248415
Java程序计算的结果: 39.90292228248415