Java题解-四则运算解析器 | 豆包MarsCode AI刷题

148 阅读3分钟

解题文章:实现一个基本的计算器

问题描述

实现一个基本的计算器来计算一个简单的字符串表达式的值。输入是一个字符串表达式,可以包含的运算符有左括号 (、右括号 )、加号 +、减号 -、乘号 * 和除号 /。可以包含的数字为非负整数(小于10)。字符串中不包含空格。处理除法时,直接省略小数部分结果,只保留整数部分参与后续运算。请不要使用内置的库函数 eval

输入格式

输入是一个字符串表达式,例如:"3+4*5/(3+2)"

数据约束

  • 字符串表达式有效。
  • 数字为非负整数,且小于10。

输出格式

计算之后的数字。

输入样例

1+1
3+4*5/(3+2)
4+2*5-2/1
(1+(4+5+2)-3)+(6+8)

输出样例

2
7
12
23

解题思路

为了实现一个基本的计算器,我们可以使用栈来处理括号和运算符的优先级。具体步骤如下:

  1. 解析表达式:遍历字符串,识别数字和运算符。
  2. 处理括号:使用栈来处理括号内的表达式。
  3. 处理运算符优先级:使用两个栈,一个用于存储操作数,另一个用于存储运算符。
  4. 计算结果:根据运算符的优先级进行计算。

代码实现

import java.util.Stack;

public class Main {
    public static int solution(String expression) {
        Stack<Integer> operands = new Stack<>();
        Stack<Character> operators = new Stack<>();
        
        for (int i = 0; i < expression.length(); i++) {
            char ch = expression.charAt(i);
            
            if (Character.isDigit(ch)) {
                // 处理多位数字
                int num = 0;
                while (i < expression.length() && Character.isDigit(expression.charAt(i))) {
                    num = num * 10 + (expression.charAt(i) - '0');
                    i++;
                }
                i--; // 回退一个位置,因为 for 循环会再加一次
                operands.push(num);
            } else if (ch == '(') {
                operators.push(ch);
            } else if (ch == ')') {
                while (!operators.isEmpty() && operators.peek() != '(') {
                    performOperation(operands, operators);
                }
                operators.pop(); // 弹出 '('
            } else if (ch == '+' || ch == '-' || ch == '*' || ch == '/') {
                while (!operators.isEmpty() && precedence(operators.peek()) >= precedence(ch)) {
                    performOperation(operands, operators);
                }
                operators.push(ch);
            }
        }
        
        while (!operators.isEmpty()) {
            performOperation(operands, operators);
        }
        
        return operands.pop();
    }

    private static void performOperation(Stack<Integer> operands, Stack<Character> operators) {
        int b = operands.pop();
        int a = operands.pop();
        char op = operators.pop();
        
        switch (op) {
            case '+':
                operands.push(a + b);
                break;
            case '-':
                operands.push(a - b);
                break;
            case '*':
                operands.push(a * b);
                break;
            case '/':
                operands.push(a / b);
                break;
        }
    }

    private static int precedence(char op) {
        switch (op) {
            case '+':
            case '-':
                return 1;
            case '*':
            case '/':
                return 2;
            default:
                return 0;
        }
    }

    public static void main(String[] args) {
        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);
    }
}

解释

  1. 解析表达式

    • 遍历字符串,识别数字和运算符。
    • 处理多位数字时,使用一个循环来读取完整的数字。
  2. 处理括号

    • 当遇到左括号 ( 时,将其压入运算符栈。
    • 当遇到右括号 ) 时,弹出运算符栈中的运算符并进行计算,直到遇到左括号 (
  3. 处理运算符优先级

    • 使用一个栈来存储运算符,并根据运算符的优先级决定是否进行计算。
    • 运算符的优先级由 precedence 方法定义,乘除运算符优先级高于加减运算符。
  4. 计算结果

    • 在遍历完表达式后,继续处理运算符栈中的运算符,直到栈为空。
    • 最终结果保存在操作数栈中,返回栈顶元素。

复杂度分析

  • 时间复杂度:O(n),其中 n 是表达式的长度。每个字符最多被处理两次(一次入栈,一次出栈)。
  • 空间复杂度:O(n),最坏情况下,所有字符都入栈。

进阶优化

  1. 避免多次字符检查:在处理多位数字时,可以使用一个标志位来标记是否正在处理数字,从而减少不必要的字符检查。
  2. 优化栈操作:在某些情况下,可以使用数组来替代栈,以减少动态内存分配的开销。
  3. 预处理表达式:对于固定的表达式格式,可以预先解析表达式,提取数字和运算符,减少运行时的解析开销。

结论

通过使用栈来处理括号和运算符的优先级,我们可以有效地实现一个基本的计算器。上述解决方案的时间复杂度为 O(n),空间复杂度为 O(n),适用于大多数简单表达式的计算。对于更复杂的表达式,可以通过进一步优化来提高性能。