数据结构与算法之计算器实现(中缀转后缀,用后缀表达式实现)

/**
 * 后缀表达式实现计算器
 */
public class SuffixExpressionCalculator {

    /**
     * 计算方法
     *
     * @param infixExpr 中缀表达式
     * @return 结果
     */
    public int calculate(String infixExpr) {
        Stack<Integer> stack = new Stack<>();
        List<String> suffixExprList = infixToSuffix(infixExpr);
        System.out.println("后缀表达式: " + suffixExprList);
        for (String str : suffixExprList) {
            char ch = str.charAt(0);
            if (!isOperator(ch)) {
                //stack.push((int) ch); 这里出问题, 举个例子: 本来应该是数字类型的5, 现在是char类型的 '5' 转换成int之后是53,肯定就不对了
                stack.push(Integer.parseInt(str));
            } else {
                int num1 = stack.pop();
                int num2 = stack.pop();
                stack.push(operate(num1, num2, ch));
            }
        }
        return stack.pop();
    }

    /**
     * 加减乘除四则运算
     *
     * @param num1     值1
     * @param num2     值2
     * @param operator 操作符
     * @return 结果
     */
    private int operate(int num1, int num2, char operator) {
        int result = 0;
        switch (operator) {
            case '+':
                result = num1 + num2;
                break;
            case '-':
                result = num2 - num1;
                break;
            case '*':
                result = num1 * num2;
                break;
            case '/':
                result = num2 / num1;
                break;
        }
        return result;
    }

    /**
     * 中缀转后缀
     *
     * @param infixExpr 中缀表达式
     * @return 后缀表达式
     */
    private List<String> infixToSuffix(String infixExpr) {
//        Stack<Integer> stackNum = new Stack<>(); 这里出现问题:如果泛型都是Integer那最终结果输出的字符char也会转为int, 导致无法分辨出数字和符号
//        Stack<Integer> stackOpe = new Stack<>();
        Stack<String> stackNum = new Stack<>();
        Stack<String> stackOpe = new Stack<>();
        int len = infixExpr.length();
        for (int i = 0; i < len; ) {
            char ch = infixExpr.charAt(i);
            String chStr = String.valueOf(ch);
            // 判断是否是操作符
            if (isOperator(ch)) { // 是操作符
                if (stackOpe.empty()) { // 是操作符且栈为空,则符号直接入栈
                    stackOpe.push(chStr);
                } else { // 如果符号栈不为空
                    char top = stackOpe.peek().charAt(0);
                    // 判断当前符号与栈顶符号的优先级
                    if (priority(ch) <= priority(top)) { // 当前符号优先级小于等于栈顶符号
                        if (top != '(') { // 栈顶符号不是左括号
                            stackNum.push(stackOpe.pop()); // 此时, 将栈顶符号取出压入数字栈
                        }
                        stackOpe.push(chStr); // 再将当前符号压入符号栈, 这里还包含一种情况是如果top=='(',则符号直接入栈
                    } else { // 当前符号优先级大于栈顶符号
                        if (ch == ')') { // 当前符号是右括号, 需要依次弹出栈中符号压入数字栈,直到左括号为止, 注意: 左括号也需要弹出,但是不入数字栈
                            while (true) {
                                String cha = stackOpe.pop();
                                if (cha.charAt(0) == '(') {
                                    //stackOpe.pop();  这里出现问题: 多取了一次
                                    break;
                                }
                                stackNum.push(cha);
                            }
                        } else { // 当前符号不是右括号, 且当前符号优先级大于栈顶符号, 则当前符号直接入栈
                            stackOpe.push(chStr);
                        }
                    }
                }
                i++; // 循环 + 1
            } else { // 字符是数字
                StringBuilder sb = new StringBuilder();
                sb.append(ch);
                // 数字有可能不是个位数, 有可能是多位数, 所以按照char遍历就会出问题, 这里while循环就是处理这种情况
                while (true) {
                    i++; // 循环 + 1
                    if (i == len) { // 处理多位数在表达式最后的情况, 如果等于了表达式的长度就退出
                        break;
                    }
                    char cha = infixExpr.charAt(i);
                    if (isOperator(cha)) { // 处理多位数在表达式中间的情况, 如果是操作符就退出
                        break;
                    }
                    sb.append(cha);
                }
                // 把得到的数字压入数字栈
                stackNum.push(sb.toString());
            }
        }
        while (!stackOpe.isEmpty()) {
            stackNum.push(stackOpe.pop());
        }
        List<String> list = new ArrayList<>();
        List<String> listData = new ArrayList<>();
        while (!stackNum.isEmpty()) {
            list.add(stackNum.pop());
        }
        // 转为逆序(逆波兰)
        for (int i = list.size() - 1; i > -1; i--) {
            listData.add(list.get(i));
        }
        return listData;
    }

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

    private int priority(char ch) {
        if (ch == '+' || ch == '-') {
            return 0;
        }
        if (ch == '*' || ch == '/') {
            return 1;
        }
        if (ch == '(' || ch == ')') {
            return 2;
        }
        return -1;
    }

    public static void main(String[] args) {
        SuffixExpressionCalculator sec = new SuffixExpressionCalculator();
        System.out.println(sec.calculate("5*(3+4)-6+80"));
    }
}
打印结果:

后缀表达式: [5, 3, 4, +, *, 6, -, 80, +]
109

之前的博客中已经把思路写出来了,这篇博客用代码来实现一下,也算自己的学习笔记。祝大家周末愉快~~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章