227. 基本计算器 II

找不到原题了,在leetcode找到类似的题目,不过leetcode的题目上没有括号的要求,代码实现中是有括号的。

题目:https://leetcode.cn/problems/basic-calculator-ii/

代码:

public class Solution {
    private static final Character add = '+';
    private static final Character sub = '-';
    private static final Character mult = '*';
    private static final Character divi = '/';
    private static final Character parenthesesLeft = '(';
    private static final Character parenthesesRight = ')';

    /**
     * 符号map key为计算符号 value为优先级
     */
    private static final Map<Character, Integer> symbolMap = new HashMap<>();

    static {
        symbolMap.put(add, 0);
        symbolMap.put(sub, 0);
        symbolMap.put(mult, 1);
        symbolMap.put(divi, 1);
        symbolMap.put(parenthesesLeft, 2);
        symbolMap.put(parenthesesRight, 2);
    }

    public int calculate(String s) {
        List<Object> expressionList = this.parseExpression(s);
        System.out.println("解析后:" + expressionList);
        expressionList = this.toReversePolishNotation(expressionList);
        System.out.println("逆波兰式:" + expressionList);
        return this.calculate(expressionList).intValue();
    }

    /**
     * 解析表达式
     *
     * @param expression
     * @return
     */
    public List<Object> parseExpression(String expression) {
        List<Object> list = new ArrayList<>();
        StringBuilder numStr = new StringBuilder();
        //去空格
        expression = expression.replaceAll(" ", "");
        //遍历字符串,提取数字和计算符号
        for (Character c : expression.toCharArray()) {
            if (symbolMap.containsKey(c)) {
                //计算符号
                if (numStr.length() > 0) {
                    //数字转Long类型
                    list.add(Long.valueOf(numStr.toString()));
                    numStr = new StringBuilder();
                }
                list.add(c);
            } else {
                //数字
                numStr.append(c);
            }
        }
        if (numStr.length() > 0) {
            //数字转Long类型
            list.add(Long.valueOf(numStr.toString()));
        }
        return list;
    }

    /**
     * 转成逆波兰式(后缀表达式)
     *
     * @param expressionList
     * @return
     */
    public List<Object> toReversePolishNotation(List<Object> expressionList) {
        List<Object> list = new ArrayList<>();
        Stack<Character> stack = new Stack<>();
        //遍历表达式并转换
        for (Object item : expressionList) {
            if (symbolMap.containsKey(item)) {
                //计算符号
                if (parenthesesLeft.equals(item)) {
                    //左括号入栈
                    stack.push((Character) item);
                } else if (parenthesesRight.equals(item)) {
                    //右括号,出栈直到碰到左括号
                    while (!stack.isEmpty() && !parenthesesLeft.equals(stack.peek())) {
                        list.add(stack.pop());
                    }
                    //将左括号弹出丢弃
                    if (!stack.isEmpty()) {
                        stack.pop();
                    }
                } else {
                    //其他符号,当前符号的优先等级小于栈内的,将栈内的全部出栈
                    while (!stack.isEmpty() && !parenthesesLeft.equals(stack.peek()) && symbolMap.get(item) <= symbolMap.get(stack.peek())) {
                        list.add(stack.pop());
                    }
                    //将当前符号入栈
                    stack.push((Character) item);
                }
            } else {
                //数字
                list.add(item);
            }
        }
        //清空栈
        while (!stack.isEmpty()) {
            list.add(stack.pop());
        }
        return list;
    }

    /**
     * 通过逆波兰式进行计算
     *
     * @param expressionList
     * @return
     */
    public Long calculate(List<Object> expressionList) {
        Stack<Long> stack = new Stack<>();
        for (Object item : expressionList) {
            if (symbolMap.containsKey(item)) {
                //计算符号
                Long num2 = stack.pop();
                Long num1 = stack.pop();
                if (add.equals(item)) {
                    stack.push(num1 + num2);
                } else if (sub.equals(item)) {
                    stack.push(num1 - num2);
                } else if (mult.equals(item)) {
                    stack.push(num1 * num2);
                } else if (divi.equals(item)) {
                    stack.push(num1 / num2);
                }
            } else {
                //数字
                stack.push((Long) item);
            }
        }
        return stack.pop();
    }

}

思路:分三步走
1、先解析整个字符串,主要是拆出数字和计算符号,因为数字是Long类型,符号是String类型,简单操作就直接用一个List<Object>类型的list接数据。
实现上比较简单,提前将计算符号用一个Map存起来(Set也可以,不过后续有需要用到计算符号的优先级,所以用Map),使用StringBuilder拼接数字,如果遇到计算符号或者字符串都读完了,就把StringBuilder的值转成Long类型数字。

2、使用逆波兰式(后缀表达式)的方法来做计算,逆波兰式相关的资料见:https://baike.baidu.com/item/%E9%80%86%E6%B3%A2%E5%85%B0%E5%BC%8F/128437
这是整个题目实现上最复杂的部分,主要是用到了Stack栈,通过符号的一个优先级(括号由于比较特殊,优先级最高,所以直接if中特殊处理的),来判断出栈或者清空Stack的时机,具体请看代码注释。

3、通过逆波兰式进行计算。
这个比较简单,还是使用到Stack栈,如果是数字就入栈,碰到计算符号就弹出前2个值,也就是出栈。然后根据计算符号的规则(加减乘除)计算后将结果再次入栈。最终栈内有且只会有一个数字,也就是答案。


觉得内容还不错?打赏个钢镚鼓励鼓励!!👍