算法:输入一个四则运算的算术表达式字符串,计算出表达式的结果(逆波兰表达式)

算法的实现思路:将算术表达式转换为逆波兰表达式,然后根据逆波兰表达式的法则计算结果

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

你可能感兴趣的:(Java工具包,算法,小工具)