52. 简单四则运算解析器|豆包MarsCode AI 刷题

64 阅读4分钟

问题描述

小F面临一个编程挑战:实现一个基本的计算器来计算简单的字符串表达式的值。该字符串表达式有效,并可能包含数字(0-9)、运算符+-及括号()。注意,字符串中不包含空格。除法运算应只保留整数结果。请实现一个解析器计算这些表达式的值,且不使用任何内置的eval函数。

测试样例

样例1:

输入:expression = "1+1"
输出:2

样例2:

输入:expression = "3+4*5/(3+2)"
输出:7

样例3:

输入:expression = "4+2*5-2/1"
输出:12

样例4:

输入:expression = "(1+(4+5+2)-3)+(6+8)"
输出:23

样例5:

输入:expression = "2*(5+5*2)/3+(6+8*3)"
输出:40

问题分析

我们需要实现一个基本的计算器来计算简单的字符串表达式的值。该字符串表达式有效,并可能包含数字(0-9)、运算符+-及括号()。注意,字符串中不包含空格。除法运算应只保留整数结果。请实现一个解析器计算这些表达式的值,且不使用任何内置的eval函数。

数据结构选择

  1. 栈(Stack)

    • 用于存储运算符和括号,以便在处理表达式时进行优先级管理。
    • 用于存储数字,以便在计算逆波兰表达式时进行操作。
  2. 字符串(String)

    • 用于存储原始表达式。
    • 用于构建逆波兰表达式(RPE)。

算法步骤

  1. 中缀表达式转逆波兰表达式(RPE)

    • 遍历输入的字符串表达式。
    • 如果当前字符是数字,则将其转换为整数并存储在临时变量中。
    • 如果当前字符是左括号(,则将其压入运算符栈。
    • 如果当前字符是运算符(+-*/),则根据运算符的优先级,将栈顶的运算符弹出并加入到逆波兰表达式中,直到栈为空或遇到优先级更低的运算符。
    • 如果当前字符是右括号),则将运算符栈中的运算符弹出并加入到逆波兰表达式中,直到遇到左括号(
    • 遍历结束后,将栈中剩余的运算符全部弹出并加入到逆波兰表达式中。
  2. 计算逆波兰表达式

    • 遍历逆波兰表达式。
    • 如果当前元素是数字,则将其压入数字栈。
    • 如果当前元素是运算符,则从数字栈中弹出两个数字,进行相应的运算,并将结果压入数字栈。
    • 遍历结束后,数字栈中剩下的唯一元素即为表达式的计算结果。

具体步骤

  1. 初始化

    • 创建两个栈:一个用于存储运算符,一个用于存储数字。
    • 创建一个字符串构建器(StringBuilder)用于构建逆波兰表达式。
  2. 中缀转逆波兰

    • 遍历输入表达式,根据字符类型进行相应处理。
    • 将数字和运算符按照优先级和括号规则加入到逆波兰表达式中。
  3. 计算逆波兰表达式

    • 遍历逆波兰表达式,根据元素类型进行相应处理。
    • 最终得到计算结果。

总结

通过上述步骤,我们可以将中缀表达式转换为逆波兰表达式,并计算出表达式的值。这个方法利用栈的特性来处理运算符的优先级和括号,确保表达式能够正确计算。

这种类型的题有个秒杀大法,就是使用逆波兰表达式RPE。

代码实现

import java.util.Stack;
public class Main {
    public static int solution(String expression) {
        // 先转化为RPE
        Stack<Character> stack = new Stack<>();
        Stack<Integer> numStack = new Stack<>();
        StringBuilder vec = new StringBuilder();
        int tempNum = 0;
        boolean flag = false; // flag 代表tempNum是否被修改

        for (int i = 0; i < expression.length(); i++) {
            char ch = expression.charAt(i);
            if (ch >= '0' && ch <= '9') {
                tempNum = tempNum * 10 + (ch - '0');
                flag = true;
            } else if (ch == '(') {
                if (flag) {
                    vec.append(tempNum).append(" ");
                    tempNum = 0;
                    flag = false;
                }
                stack.push(ch);
            } else if (ch == '+' || ch == '-') {
                if (flag) {
                    vec.append(tempNum).append(" ");
                    tempNum = 0;
                    flag = false;
                }
                while (!stack.isEmpty() && (stack.peek() == '+' || stack.peek() == '-' || stack.peek() == '*' || stack.peek() == '/')) {
                    vec.append(stack.pop()).append(" ");
                }
                stack.push(ch);
            } else if (ch == '*' || ch == '/') {
                if (flag) {
                    vec.append(tempNum).append(" ");
                    tempNum = 0;
                    flag = false;
                }
                while (!stack.isEmpty() && (stack.peek() == '*' || stack.peek() == '/')) {
                    vec.append(stack.pop()).append(" ");
                }
                stack.push(ch);
            } else if (ch == ')') {
                if (flag) {
                    vec.append(tempNum).append(" ");
                    tempNum = 0;
                    flag = false;
                }
                while (!stack.isEmpty() && stack.peek() != '(') {
                    vec.append(stack.pop()).append(" ");
                }
                stack.pop();
            }
        }

        if (flag) {
            vec.append(tempNum).append(" ");
            tempNum = 0;
            flag = false;
        }
        while (!stack.isEmpty()) {
            vec.append(stack.pop()).append(" ");
        }

        // 执行至此,原中缀表达式已转换为逆波兰表达式并存储至vec中
        String[] tokens = vec.toString().split(" ");
        for (String token : tokens) {
            if (token.equals("+")) {
                int ops1 = numStack.pop();
                int ops2 = numStack.pop();
                int tempAns = ops2 + ops1;
                numStack.push(tempAns);
            } else if (token.equals("-")) {
                int ops1 = numStack.pop();
                int ops2 = numStack.pop();
                int tempAns = ops2 - ops1;
                numStack.push(tempAns);
            } else if (token.equals("*")) {
                int ops1 = numStack.pop();
                int ops2 = numStack.pop();
                int tempAns = ops2 * ops1;
                numStack.push(tempAns);
            } else if (token.equals("/")) {
                int ops1 = numStack.pop();
                int ops2 = numStack.pop();
                int tempAns = ops2 / ops1;
                numStack.push(tempAns);
            } else { // 是数字
                numStack.push(Integer.parseInt(token));
            }
        }
        return numStack.pop();
    }

    public static void main(String[] args) {
        // You can add more test cases here
        System.out.println(solution("1+1") == 2);
        System.out.println(solution("3+4*5/(3+2)") == 7);
        System.out.println(solution("4+2*5-2/1") == 12);
        System.out.println(solution("(1+(4+5+2)-3)+(6+8)") == 23);
    }
}