逆波兰式计算

import java.util.Stack;
import java.util.StringTokenizer;

/**
 * 逆波兰式计算
 * java 运算公式运算得出结果  可以计算负数,保留小数位数
 * @Date: 2018/9/6 11:41
 * @Description:
 */
public class ReversePolishCalculate {

    private static final int One = 1;      //规定优先级    最低
    private static final int Two = 2;     //
    private static final int Three = 3;   //规定优先级   Three 最高
    private static final int SCALE = 10; //保留小数位数
    /**
     * 计算 逆波兰式
     * @param formulaStr
     * @return
     * @throws Exception
     */
    public static String reversePolishCalculate(String formulaStr) throws Exception {
        Stack<String> ysStack = new Stack<>(); //  定义一个运算栈
        ArrayList<String> nblbdsList = zzHz(formulaStr);//得到逆波兰后缀
        for(String nblbdsStr:nblbdsList){
            if(isNum(nblbdsStr)){
                ysStack.push(nblbdsStr);
            }else{
                ysStack.push(String.valueOf(calculate(ysStack.pop(), ysStack.pop(), nblbdsStr)));
            }
        }
        return ysStack.pop();  //最后 栈中元素 即为结果
    }
    /**
     * 判断 是否是数字
     * @param str
     * @return
     */
    public static boolean isNum(String str){
        if(str.matches("([1-9]\\d*\\.?\\d*)|(0\\.\\d*[0-9])|(0)")){    //这里使用正则表达式 验证是否是数字包含小数,0
            return true;
        }
        return false;
    }

    /**
     * 判断 是否是操作符
     * @param str
     * @return
     */
    public static boolean isCzf(String str){
        switch(str){
            case "(":
            case "*":
            case "/":
            case "+":
            case "-":
            case ")":return true;
            default : return false;
        }
    }

    /**
     * 获取 优先级
     * @param str
     * @return
     */
    private static int getYxj(String str){

        switch(str){
            case "(":return Three;
            case "*":
            case "/":return Two;
            case "+":
            case "-":return One;
            case ")":return 0;
            default : return -1;

        }

    }

    /**
     * 判断优先级
     * @param str1
     * @param str2
     * @return
     */
    private static boolean isYxj(String str1,String str2){
        return getYxj(str1) > getYxj(str2);
    }

    /**
     * 数字直接入nblbdsList
     * 运算符要与operators栈顶比较,优先级大则入栈,小于或等于但不是)则operators出栈到(后该操作符再入栈,如果是)则到(之后
     * operators栈顶若是(则无条件入栈
     * ********* 当 当前操作元素为 操作符时**********    这里是 核心代码, 用于操作符栈的判断
     * @param czf
     * @param nblbdsList
     * @param czfStack
     */
    private static void stackCzf(String czf,ArrayList<String> nblbdsList,Stack<String> czfStack){

        //判断当前栈内是否为空
        if(czfStack.isEmpty()){
            czfStack.push(czf);
            return;
        }

        //判断是否为 (
        if("(".equals(czf)){
            czfStack.push(czf);
            return;
        }

        //判断是否为 )
        if(")".equals(czf)){
            String string = "";
            while(!"(".equals(string = czfStack.pop())){
                nblbdsList.add(string);
            }
            return;
        }

        //如果 当前栈顶元素是  (  直接入栈
        if("(".equals(czfStack.peek())){
            czfStack.push(czf);
            return;
        }

        // 判断 与 栈顶元素的优先级 , > 为true
        if(isYxj(czf, czfStack.peek())){
            czfStack.push(czf);
            return;
        }

        if(!isYxj(czf, czfStack.peek())){
            nblbdsList.add(czfStack.pop());
            stackCzf(czf,nblbdsList,czfStack);   //这里调用函数 本身,并将本次的操作数传参
        }

    }

    /**
     *
     * 逆波兰转换
     * 中缀 —> 后缀
     * @param formulaStr
     * @return
     * @throws Exception
     */
    private static ArrayList<String> zzHz(String formulaStr) throws Exception {
        ArrayList<String> nblbdsList = new ArrayList<>();      // 存放转换后的 逆波兰式
        Stack<String> czfStack = new Stack<>();        // 存放 运算符的栈
        StringTokenizer stringTokenizer = new StringTokenizer(formulaStr, " +-*/()",true);
        Boolean preFlag = false;
        while(stringTokenizer.hasMoreTokens()) { //字符串分割
            String currStr = stringTokenizer.nextToken();
            if(isNum(currStr)){
                nblbdsList.add(currStr);
                preFlag = false;
                continue;
            }
            if(isCzf(currStr)){
                if("-".equals(currStr)&&preFlag){
                    nblbdsList.add("0");
                }
                stackCzf(currStr,nblbdsList,czfStack);
                preFlag = true;
                continue;
            }
            if(" ".equals(currStr)){
                continue;
            }
            throw new Exception("非法字符:"+currStr);
        }

        // 遍历完原始表达式后  将操作符栈内元素 全部添加至 逆波兰表达式list
        while(!czfStack.isEmpty()){
            nblbdsList.add(czfStack.pop());
        }
        return nblbdsList;
    }

    /**
     * 具体计算方法
     * @param s1
     * @param s2
     * @param s3
     * @return
     */
    private static BigDecimal calculate(String s1, String s2, String s3){
        BigDecimal one= new BigDecimal(s2);
        BigDecimal two= new BigDecimal(s1);
        switch(s3){
            case "+":return one.add(two);
            case "-":return one.subtract(two);
            case "*":return one.multiply(two);
            case "/":return one.divide(two,SCALE,BigDecimal.ROUND_HALF_UP);
            default : return BigDecimal.ZERO;
        }
    }

    public static void main(String[] ars){
        try {
            String a=  ReversePolishCalculate.reversePolishCalculate("1+(1+1.5)");
            System.out.println(a);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

posted @ 2020-04-05 15:15  爱上胡萝卜的猴子  阅读(271)  评论(0编辑  收藏  举报