逆波兰式实现四则运算(加减乘除)

最近做一个项目需要做表达式的解析,初想不难,仔细研究之后,发现做细点可能会涉及到编译原理的词法解析和语法解析。但是如果只做简单的表达式计算,可以使用逆波兰式。
何为逆波兰式,可以看这个链接:https://baike.baidu.com/item/%E9%80%86%E6%B3%A2%E5%85%B0%E5%BC%8F/128437?fr=aladdin
简单研究了一下,下面是实现的简单四则运算:

public class RPNUtils {
    private static final Logger logger = LoggerFactory.getLogger(RPNUtils.class);
    /**
     * 运算符优先级map(数字越大,优先级越高)
     */
    static Map priorityMap = new HashMap<>(4);
    static{
        priorityMap.put("+",1);
        priorityMap.put("-",1);
        priorityMap.put("*",2);
        priorityMap.put("/",2);
        priorityMap.put("(",0);
    }

    /**
     * 计算表达式
     * @param exp(中缀表达式)
     * @return 计算结果
     */
    public static Double calExp(String exp){
        try{
            logger.info("传入计算表达式:{}", exp);
            List rpnList = transToRPN(exp);
            logger.info("逆波兰式为:{}",JSON.toJSONString(rpnList));
            Double result = cal(rpnList);
            logger.info("计算结果为:{}",result);
            return result;
        }catch (Exception e){
            e.printStackTrace();
            logger.error("表达式计算异常:"+exp,e);
        }
        return 0.0;
    }

    /**
     * 转化成逆波兰式
     * @param exp
     * @return
     */
    public static List transToRPN(String exp){
        //操作数栈
        Stack numStack = new Stack<>();
        //运算符栈
        Stack operStack = new Stack<>();
        //转化成字符数组
        char[] expArray = exp.toCharArray();
        StringBuffer sb = new StringBuffer();
        List numOperList = new ArrayList<>();
        //分离运算符和操作数(在运算符和操作数之间添加空格符)
        for (int i = 0; i < expArray.length; i++){
            if (Character.isDigit(expArray[i]) || ".".equals(String.valueOf(expArray[i]))){
                sb.append(expArray[i]);
            }else{
                sb.append(" ").append(expArray[i]).append(" ");
            }
        }
        //得到操作数和运算符的数组
        String[] array = sb.toString().trim().split(" ");
        //转化成list
        numOperList = Arrays.asList(array);
        for (int i = 0; i < numOperList.size(); i++) {
            String a = numOperList.get(i);
            //过滤空字符串
            if (a.equals("")){
                continue;
            }
            if (Character.isDigit(a.charAt(0))){
                //如果是操作数直接放入操作数栈
                numStack.push(a);
            }else{
                if (operStack.isEmpty()){
                    //如果是运算符,且运算符栈是空的,直接将运算符放入运算符栈
                    operStack.push(a);
                    continue;
                }
                if (a.equals("(")){
                    //如果是左括号,直接放入运算符栈
                    operStack.push(a);
                    continue;
                }
                if (a.equals(")")){
                    //如果是右括号,则运算符栈依次出栈,并放入操作数栈,直到出栈运算符为左括号,并舍弃左括号
                    while (!operStack.peek().toString().equals("(")){
                        numStack.push(operStack.pop());
                    }
                    operStack.pop();
                    continue;
                }
                //比较当前运算符和运算符栈顶的运算符
                //如果栈顶运算符为左括号,则直接放入运算符栈
                String topOper = operStack.peek();
                if (topOper.equals("(")){
                    operStack.push(a);
                    continue;
                }
                //比较当前运算符的优先级和运算符栈顶运算符的优先级,若大于(等于)栈顶运算符优先级,则直接放入运算符栈
                if (priorityMap.get(a.toString()) > priorityMap.get(topOper.toString())){
                    operStack.push(a);
                }else{
                    // 否则,运算符栈顶运算符出栈并放入操作数栈,直到运算符栈顶操作符优先级低于(不包含等于)该运算符优先级
                    do{
                        numStack.push(operStack.pop());
                    }while(!operStack.isEmpty() && priorityMap.get(operStack.peek()) >= priorityMap.get(a));
                    //最后当前运算符放入运算符栈
                    operStack.push(a);
                }

            }
        }
        //如果上面步骤走完了,运算符栈中还有运算符,则依次出栈,放入到操作数栈
        while(!operStack.isEmpty()){
            numStack.push(operStack.pop());
        }
        return new ArrayList<>(numStack);
    }

    /**
     * 根据逆波兰式计算表达式结果
     * @param list
     * @return
     */
    public static Double cal(List list){
        System.out.println(JSON.toJSONString(list));
        //计算栈
        Stack s = new Stack<>();
        for (int i = 0; i < list.size(); i++) {
            String a = list.get(i);
            if (Character.isDigit(a.charAt(0))){
                //如果是数字,则直接入栈
                s.push(a);
            }else{
                //如果是操作符,则计算栈栈顶两个元素依次出栈,进行计算,然后将计算结果入栈
                if (a.equals("+")){
                    Double result = Double.valueOf(s.pop()) + Double.valueOf(s.pop());
                    s.push(String.valueOf(result));
                }
                if (a.equals("-")){
                    Double s1 = Double.valueOf(s.pop());
                    Double s2 = Double.valueOf(s.pop());
                    s.push(String.valueOf(s2 - s1));
                }
                if (a.equals("*")){
                    Double s1 = Double.valueOf(s.pop());
                    Double s2 = Double.valueOf(s.pop());
                    s.push(String.valueOf(s1*s2));
                }
                if (a.equals("/")){
                    Double s1 = Double.valueOf(s.pop());
                    Double s2 = Double.valueOf(s.pop());
                    s.push(String.valueOf(s2 / s1));
                }
            }
        }
        //计算完成,栈中元素便是计算结果
        return Double.valueOf(s.pop());
    }
    public static void main(String[] args) {
        //String str = "(50-50*50/50)*5.1/5.1+50*20+15.5*(20-10)/5.0-(2-1)*(5/1.0)-2+(2*2)/ 4.0";
        //String str = "10-1*5-2";
        String str = "(10-3-2-3-1)*(20-10-5)*(3-2-2+2+4)";
        System.out.println(calExp(str));
    }
}

你可能感兴趣的:(算法,java)