2020-09-02

模拟一个简单的逆波兰计算器(后缀表达式计算)

可以简单实现加、 减、乘、除、带括号和多位数的运算,不支持小数点(因为懒)。有详细的代码注释,有些地方可能没有注释到,请见谅。

package com.java.stack;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 * 逆波兰计算器
 */
public class StackCal {
    public static void main(String[] args) {
        doNiBolan("1 + ( ( 2 + 3 ) * 4) - 5");
    }

    /**
     * 将普通的中缀表达式转化成后缀表达式(逆波兰表达式)
     * @param expression 传入的中缀表达式
     * @return 转换后的逆波兰表达式
     */
    private static String toNiBolan(String expression) {
        //转换成标准的表达式
        List<String> stanExp = toStandardExpression(expression);

        //开始真正转换
        //1 + ( ( 2 + 3 ) * 4) - 5     ->      1 2 3 + 4 * + 5 -

        //创建操作符栈和数栈
        Stack<String> opeStack = new Stack<>();
        Stack<String> numStack = new Stack<>();

        //遍历标准表达式集合
        for (String item : stanExp) {
            if(item.matches("\\d+")){
                //是数字,直接入数栈
                numStack.push(item);
            }else if(opeStack.empty() || item.equals("(")){
                //栈是空的,或者匹配的是左括号,就直接入栈
                opeStack.push(item);
            }else if(item.equals(")")){
                //匹配到右括号,从操作符栈弹出所有操作符到数栈,直到匹配到第一个左括号
                while (!opeStack.peek().equals("(")){
                    numStack.push(opeStack.pop());
                }
                //最后把操作符栈的左括号移除,这一步非常重要
                opeStack.pop();
            }else if(Priority.getValue(item) > Priority.getValue(opeStack.peek())){
                //进来的运算符优先级比栈顶的高,且不是右括号,也直接入操作符栈
                opeStack.push(item);
            }else {
                //如不高,从操作符栈弹出一个如数栈,再将新来的item如操作符栈
                numStack.push(opeStack.pop());
                opeStack.push(item);
            }
        }
        //把最后剩在操作符栈的元素如数栈
        for (String s : opeStack) {
            numStack.push(s);
        }
        //对最终要返回的结果进行处理,比如去除两边的"[","]",和","
        String s = numStack.toString();
        String retStr = s.replace("[","")
                .replace(",","")
                .replace("]","");
        System.out.println("转换成的逆波兰表达式是:" + retStr);
        return retStr;
    }


    /**
     * 将表达式转换成List集合,并且支持多位数,支持过滤空格,换行符,tab
     * @param expression
     * @return 处理之后的list集合
     */
    public static List<String> toStandardExpression(String expression){
        //去除expression中的多余的空格,换行符,等等
        String newExp = expression
                .replace(" ", "")
                .replace("\n", "")
                .replace("\t","")
                .replace("\r","")
                .trim();
        List<String> retExp = new ArrayList<>();
        int index = 0;
        for(int i = 0; i < newExp.length();){
            char c = newExp.charAt(i);
            if(c >= 48 && c <= 57){
                //说明该位置是一个数字
                String str = "";
                str += c;
                i++;
                while( i < newExp.length() && newExp.charAt(i) >= 48 && newExp.charAt(i) <= 57){
                    str += newExp.charAt(i);
                    i++;
                }
                retExp.add(str);
            }else {
                //非数字进行操作
                retExp.add(""+c);
                i++;
            }
        }
        System.out.println("标准表达式数组:" + retExp.toString());
        return retExp;
    }

    /**
     * 此处是计算逆波兰表达式的方法,假设传入的表达式是标准写法:都有空格把每个元素隔开
     * @param expression 传入的逆波兰表达式
     */
    public static void doNiBolan(String expression){
        String newExp = toNiBolan(expression);
        String[] splitString = newExp.split(" ");

        Stack<String> numStack = new Stack<>();

        for (int i = 0; i < splitString.length; i++) {
            String s = splitString[i];
            //如果是一个数字
            if(!isOperator(s)){
                numStack.push(s);
            }else {
                //是一个运算符
                String num2 = numStack.pop();
                String num1 = numStack.pop();
                String res = calc(num1, num2, s);
                numStack.push(res);
            }
        }

        System.out.println("使用逆波兰计算的结果是:" + numStack.pop());

    }

    /**
     * 判断是否是 + - * / 四种运算符
     * 虽然要考虑括号情况,但是逆波兰表达式并没有括号,所以不用判断
     * @param s
     * @return
     */
    public static boolean isOperator(String s){
        boolean flag = false;
        if(s.equals("+") || s.equals("-") || s.equals("/") || s.equals("*")){
            flag = true;
        }
        return flag;
    }

    /**
     * 计算加减乘除的方法
     * @param num1 第一个数
     * @param num2 第二个数
     * @param operator 运算符
     * @return
     */
    public static String calc(String num1, String num2, String operator){
        if(operator.equals("+")){
            Integer i = Integer.parseInt(num1) + Integer.parseInt(num2);
            return i.toString();
        }else if(operator.equals("-")){
            Integer i = Integer.parseInt(num1) - Integer.parseInt(num2);
            return i.toString();
        }else if(operator.equals("*")){
            Integer i = Integer.parseInt(num1) * Integer.parseInt(num2);
            return i.toString();
        }else if(operator.equals("/")){
            Integer i = Integer.parseInt(num1) / Integer.parseInt(num2);
            return i.toString();
        }
        return null;
    }
}

/**
 * 一个计算优先级的类
 */
class Priority{
    private static int ADD = 1;
    private static int SUB = 1;
    private static int MUL = 2;
    private static int DIV = 2;

    public static int getValue(String operation) {
        int result = 0;
        switch (operation) {
            case "+":
                result = ADD;
                break;
            case "-":
                result = SUB;
                break;
            case "*":
                result = MUL;
                break;
            case "/":
                result = DIV;
                break;
            default:
                break;
        }
        return result;
    }
}

最后运行的结果
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值