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

49 阅读3分钟

问题描述

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


难点分析:

  1. 运算符优先级

    • +- 的优先级低于 */,需要正确处理优先级关系。
    • 括号 () 优先级最高,括号内的表达式应该首先计算。
    • 所以需要在处理表达式时,管理运算符的优先级以及括号的解析。
  2. 括号的嵌套

    • 括号可能嵌套,意味着你需要使用递归或栈来处理括号内的子表达式。
  3. 整数除法

    • 除法操作需要做整数除法,即忽略小数部分,确保取整。

思路

  1. 解析表达式:将表达式分解为数字和运算符。
  2. 处理运算符优先级:使用两个栈,一个用于存储数字,另一个用于存储运算符。
  3. 处理括号:遇到左括号时,将运算符压入栈;遇到右括号时,弹出运算符直到遇到左括号。
  4. 计算结果:根据运算符的优先级进行计算,并将结果压入数字栈。

关键步骤

  1. 解析数字:使用Character.isDigit判断字符是否为数字,并解析完整的数字。
  2. 处理括号:遇到左括号时,直接压入运算符栈;遇到右括号时,弹出运算符直到遇到左括号。
  3. 处理运算符优先级:使用precedence函数定义运算符的优先级,并根据优先级进行计算。
  4. 计算结果:使用applyOperation函数计算两个数的运算结果。

必备知识点:

栈的应用

  • 用途:栈是一种后进先出(LIFO,Last In First Out)的数据结构。在这个问题中,栈用来保存计算中的中间结果,特别是处理括号时。

  • 实现细节:当遇到左括号 ( 时,我们将当前的计算结果和运算符(即符号)压入栈中,遇到右括号 ) 时,弹出栈中的内容以恢复计算状态。这确保了括号内的表达式先被计算。

  • 栈操作

    • stack.push(x):将 x 压入栈。
    • stack.pop():弹出栈顶元素。
  • 栈的核心作用:用于存储括号前的结果和符号,以便在遇到右括号时,能够恢复计算的状态。


完整代码:

import java.util.Stack;

public class Main {
    public static int solution(String expression) {
        // 用于存储数字的栈
        Stack<Integer> numbers = 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循环会自动i++,所以这里要减1
                numbers.push(num);
            }
            // 如果是左括号,直接压入运算符栈
            else if (ch == '(') {
                operators.push(ch);
            }
            // 如果是右括号,计算直到遇到左括号
            else if (ch == ')') {
                while (!operators.isEmpty() && operators.peek() != '(') {
                    // 计算并压入结果
                    numbers.push(applyOperation(operators.pop(), numbers.pop(), numbers.pop()));
                }
                operators.pop(); // 弹出左括号
            }
            // 如果是运算符,处理优先级
            else if (ch == '+' || ch == '-' || ch == '*' || ch == '/') {
                while (!operators.isEmpty() && precedence(operators.peek()) >= precedence(ch)) {
                    // 计算并压入结果
                    numbers.push(applyOperation(operators.pop(), numbers.pop(), numbers.pop()));
                }
                operators.push(ch);
            }
        }
        
        // 处理剩余的运算符
        while (!operators.isEmpty()) {
            numbers.push(applyOperation(operators.pop(), numbers.pop(), numbers.pop()));
        }
        
        // 最终结果在数字栈的栈顶
        return numbers.pop();
    }
    
    // 计算两个数的运算结果
    private static int applyOperation(char operator, int b, int a) {
        switch (operator) {
            case '+':
                return a + b;
            case '-':
                return a - b;
            case '*':
                return a * b;
            case '/':
                return a / b; // 注意:这里只保留整数结果
        }
        return 0;
    }
    
    // 定义运算符的优先级
    private static int precedence(char operator) {
        if (operator == '+' || operator == '-')
            return 1;
        if (operator == '*' || operator == '/')
            return 2;
        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);
    }
}