1. java使用双栈实现一个计算器

基本逻辑

  • 双栈分别存储数值(nums)和操作符(ops
  • 在实现时需要注意优先级
  • 如果当前操作符是 ‘)’ 时,需要在遍历过程中把括号内数据做一次计算
  • 如果当前遍历的操作符如果是 + - ,且操作符栈ops的操作符是 * /,则需要优先计算 */的逻辑
  • 上边都做完的话,最终都是简单的 + - 逻辑了,直接计算即可

BigDicemal版本

public class calculatorBigdecimal {


    public static void main(String[] args) {
        String expression0 = "(2-5+5)*2-3";
        System.out.println("(2-5+5)*2-3 = " + calculate(expression0));

        String expression1 = "(2*5+5)*2-3";
        System.out.println("(2*5+5)*2-3 = " + calculate(expression1));

        String expression2 = "10+5*(2-2)";
        System.out.println("10+5*(2-2) = " + calculate(expression2));

        String expression3 = "10+5/(4-1)"; //除不尽转化为BigDecimal
        System.out.println("10+5/(4-1) = " + calculate(expression3));

        String expression6 = "2-5+2-3";
        System.out.println("2-5+2-3 = " + calculate(expression6));

    }

    public static BigDecimal calculate(String expression) {
        Stack<BigDecimal> nums = new Stack<>();
        Stack<Character> ops = new Stack<>();
        for (int i = 0; i < expression.length(); i++) {
            char c = expression.charAt(i);
            if (Character.isDigit(c)) {
                int j = i;
                while (j < expression.length() && Character.isDigit(expression.charAt(j))) {
                    j++;
                }
                nums.push(new BigDecimal(expression.substring(i, j)));
                i = j - 1;
            } else if (c == '(') {
                ops.push(c);
            } else if (c == ')') {
                while (ops.peek() != '(') {
                    nums.push(calculateByOps(ops.pop(), nums.pop(), nums.pop()));
                }
                ops.pop();
            } else if (isOperator(c)) {
                while (!ops.empty() && needCalculatePre(c, ops.peek())) {
                    nums.push(calculateByOps(ops.pop(), nums.pop(), nums.pop()));
                }
                ops.push(c);
            }
        }
        while (!ops.empty()) {
            nums.push(calculateByOps(ops.pop(), nums.pop(), nums.pop()));
        }
        return nums.pop();
    }

    private static boolean isOperator(char c) {
        return c == '+' || c == '-' || c == '*' || c == '/';
    }

    private static boolean needCalculatePre(char op1, char op2) {
        if (op2 == '(' || op2 == ')') {
            return false;
        }
        return (op2 == '*' || op2 == '/') && (op1 == '+' || op1 == '-') || (op2 == '+' || op2 == '-') && (op1 == '+' || op1 == '-');
    }

    private static BigDecimal calculateByOps(char op, BigDecimal b, BigDecimal a) {
        switch (op) {
            case '+':
                return a.add(b);
            case '-':
                return a.subtract(b);
            case '*':
                return a.multiply(b).setScale(4, BigDecimal.ROUND_HALF_UP);
            case '/':
                if (b.compareTo(BigDecimal.ZERO) == 0) {
                    throw new UnsupportedOperationException("Cannot divide by zero");
                }
                return a.divide(b,4,  RoundingMode.HALF_UP);
            default:
                throw new UnsupportedOperationException("Unknown operator " + op);
        }
    }

}

Double版本

public class calculator2 {
    public static void main(String[] args) {
        String expression0 = "(2-5+5)*2-3";
        System.out.println("(2-5+5)*2-3 = " + calculate(expression0));

        String expression1 = "(2*5+5)*2-3";
        System.out.println("(2*5+5)*2-3 = " + calculate(expression1));

        String expression2 = "10+5*(2-2)";
        System.out.println("10+5*(2-2) = " + calculate(expression2));

        String expression3 = "10+5/(4-1)"; //除不尽转化为BigDecimal
        System.out.println("10+5/(4-1) = " + new BigDecimal(calculate(expression3)).setScale(4, BigDecimal.ROUND_HALF_UP));
    }

    public static Double calculate(String expression) {
        //第一个栈:存数字
        Stack<Double> nums = new Stack<>();
        //第二个栈:存操作符合
        Stack<Character> ops = new Stack<>();
        for (int i = 0; i < expression.length(); i++) {
            char c = expression.charAt(i);
            if (Character.isDigit(c)) {
                int j = i;
                //如果是数字,下标右移,拼接第一个数字,存入数字栈nums中
                while (j < expression.length() && Character.isDigit(expression.charAt(j))) {
                    j++;
                }
                //把第一个数字压栈
                nums.push(Double.parseDouble(expression.substring(i, j)));
                //重置下角标为最新位置,继续向下遍历
                i = j - 1;
            } else if (c == '(') {
                //如果是'(' ,直接压入操作符栈ops
                ops.push(c);
            } else if (c == ')') {
                //如果是')' ,弹出数字栈nums中两个值,弹出一个操作符栈ops的一个操作符,计算括号内的值
                while (ops.peek() != '(') {
                    nums.push(applyOp(ops.pop(), nums.pop(), nums.pop()));
                }
                //最后弹出操作符栈ops的'('
                ops.pop();
            } else if (isOperator(c)) {
                //如果是操作符号,当前遍历的操作符如果是 + - ,且操作符栈ops的操作符是  * /,则需要优先计算 *和/的逻辑
                while (!ops.empty() && hasPrecedence(c, ops.peek())) {
                    nums.push(applyOp(ops.pop(), nums.pop(), nums.pop()));
                }
                //最后再把当前遍历的 + - 操作符,压入操作符栈ops
                ops.push(c);
            }
        }
        //最终都剩下了 + - 操作符,计算剩下的内容
        while (!ops.empty()) {
            nums.push(applyOp(ops.pop(), nums.pop(), nums.pop()));
        }
        return nums.pop();
    }

    private static boolean isOperator(char c) {
        return c == '+' || c == '-' || c == '*' || c == '/';
    }

    private static boolean hasPrecedence(char op1, char op2) {
        if (op2 == '(' || op2 == ')') {
            return false;
        }
        //第一个条件是乘除法 和 加减法的优先级问题
        //第二个条件是避免(2-5+5)*2-3 这种括号内 会先计算5+5的问题
        return (op2 == '*' || op2 == '/') && (op1 == '+' || op1 == '-') ||  (op2 == '+' || op2 == '-') && (op1 == '+' || op1 == '-') ;
    }

    private static Double applyOp(char op, Double b, Double a) {
        switch (op) {
            case '+':
                return a + b;
            case '-':
                return a - b;
            case '*':
                return a * b;
            case '/':
                if (b == 0) {
                    throw new UnsupportedOperationException("Cannot divide by zero");
                }
                return a / b;
            default:
                throw new UnsupportedOperationException("Unknown operator " + op);
        }
    }

}

以上使用双栈实现的计算器,测试代码运行结果如下

java实现一个计算器:输入计算公式即可输出结果_操作符