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

Posted ZK_小姜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了逆波兰式实现四则运算(加减乘除)相关的知识,希望对你有一定的参考价值。

最近做一个项目需要做表达式的解析,初想不难,仔细研究之后,发现做细点可能会涉及到编译原理的词法解析和语法解析。但是如果只做简单的表达式计算,可以使用逆波兰式。
何为逆波兰式,可以看这个链接: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<String,Integer> 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<String> 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<String> transToRPN(String exp)
        //操作数栈
        Stack<String> numStack = new Stack<>();
        //运算符栈
        Stack<String> operStack = new Stack<>();
        //转化成字符数组
        char[] expArray = exp.toCharArray();
        StringBuffer sb = new StringBuffer();
        List<String> 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<String> list)
        System.out.println(JSON.toJSONString(list));
        //计算栈
        Stack<String> 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));
    

以上是关于逆波兰式实现四则运算(加减乘除)的主要内容,如果未能解决你的问题,请参考以下文章

[C++]利用逆波兰式,简单实现下加减乘除的混合运算

逆波兰算术表达式 C语言

现代软件工程课程作业 第一章第1题

逆波兰表达式

基于MFC的含四则混合运算的计算器

使用逆波兰式进行表达式求值