AI刷题题解(二)| 豆包MarsCode AI刷题

88 阅读5分钟

简单四则运算解析器之题解

题目概述

小F面临一个编程挑战:实现一个基本的计算器来计算简单的字符串表达式的值。该字符串表达式有效,并可能包含数字(0-9)、运算符+-及括号()

限制是
  • 字符串中不包含空格。除法运算应只保留整数结果。实现一个解析器计算这些表达式的值,且不使用任何内置的eval函数。
  • 测试样例
  1. 样例1: 输入:expression = "1+1"
    输出:2
  2. 样例2: 输入:expression = "3+4*5/(3+2)"
    输出:7

解题思路

数据结构的选择

  • :栈是一种非常适合处理表达式计算的数据结构,尤其是按照题意需要处理括号和运算符优先级时。本题可以使用两个栈:一个用于存储操作数(数字),另一个用于存储运算符。

算法实现步骤

  1. 初始化栈:创建两个栈,一个用于存储操作数(数字),另一个用于存储运算符。
  2. 遍历表达式:从左到右遍历表达式中的每一个字符。
  • 如果遇到数字,将其压入操作数栈。
  • 如果遇到运算符,将其与运算符栈顶的运算符进行比较,如果当前运算符优先级低于或等于栈顶运算符,则弹出栈顶运算符并进行计算,直到满足条件为止,然后将当前运算符压入栈。
  • 如果遇到左括号 (,直接将其压入运算符栈。
  • 如果遇到右括号 ),则弹出运算符栈中的运算符并进行计算,直到遇到左括号 ( 为止。
  1. 计算结果:遍历结束后,如果运算符栈中还有运算符,则依次弹出并进行计算,直到运算符栈为空。
    1. 返回结果:最终操作数栈中的唯一元素即为表达式的计算结果。

实现代码

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)) {
                // 处理数字
                operands.push(ch - '0');
            } else if (ch == '(') {
                // 处理左括号
                operators.push(ch);
            } else if (ch == ')') {
                // 处理右括号
                while (operators.peek() != '(') {
                    // 计算括号内的表达式
                    calculate(operands, operators);
                }
                operators.pop(); // 弹出左括号
            } else if (ch == '+' || ch == '-' || ch == '*' || ch == '/') {
                // 处理运算符
                while (!operators.isEmpty() && precedence(operators.peek()) >= precedence(ch)) {
                    // 计算优先级高的运算符
                    calculate(operands, operators);
                }
                operators.push(ch);
            }
        }
        
        // 处理剩余的运算符
        while (!operators.isEmpty()) {
            calculate(operands, operators);
        }
        
        return operands.pop();
    }
    
    private static void calculate(Stack<Integer> operands, Stack<Character> operators) {
        char op = operators.pop();
        int b = operands.pop();
        int a = operands.pop();
        int result = 0;
        
        switch (op) {
            case '+':
                result = a + b;
                break;
            case '-':
                result = a - b;
                break;
            case '*':
                result = a * b;
                break;
            case '/':
                result = a / b; // 整数除法
                break;
        }
        
        operands.push(result);
    }
    
    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);
    }
}

输出范例

true
true
true
true

具体运行过程

  1. 初始化栈

    • 创建两个栈:operands 用于存储操作数(数字),operators 用于存储运算符。
  2. 遍历表达式

    • 使用 for 循环遍历表达式中的每一个字符。
  3. 处理数字

    • 如果当前字符是数字(Character.isDigit(ch) 返回 true),将其转换为整数并压入 operands 栈。
    • 例如,对于表达式 "1+1",当遍历到 '1' 时,operands 栈会压入 1
  4. 处理左括号 (

    • 如果当前字符是左括号 '(',直接将其压入 operators 栈。
    • 例如,对于表达式 "(1+2)",当遍历到 '('1. - 时,operators 栈会压入 '('
  5. 处理右括号 )

    • 如果当前字符是右括号 ')',则不断从 operators 栈中弹出运算符并进行计算,直到遇到左括号 '(' 为止。最后弹出左括号。
    • 例如,对于表达式 "(1+2)",当遍历到 ')' 时,会弹出 '+' 并计算 1 + 2,结果 3 压入 operands 栈,最后弹出 '('
  6. 处理运算符

  • 如果当前字符是运算符('+''-''*''/'),则将其与 operators 栈顶的运算符进行比较:

    • 如果栈顶运算符的优先级大于或等于当前运算符,则弹出栈顶运算符并进行计算,直到栈顶运算符的优先级小于当前运算符。
    • 将当前运算符压入 operators 栈。
  • 例如,对于表达式 "3+4*5",当遍历到 '*' 时,会先计算 4 * 5,结果 20 压入 operands 栈,然后将 '+' 压入 operators 栈。

  1. 计算结果

    • 遍历结束后,如果 operators 栈中还有运算符,则依次弹出并进行计算,直到 operators 栈为空。
    • 例如,对于表达式 "3+4*5",遍历结束后,operators 栈中还有 '+',会计算 3 + 20,结果 23 压入 operands 栈。
  2. 返回结果

    • 最终 operands 栈中的唯一元素即为表达式的计算结果。
    • 例如,对于表达式 "3+4*5",最终 operands 栈中只有一个元素 23,返回 23

关键方法

  1. calculate 方法

    • 从 operands 栈中弹出两个操作数 a 和 b,从 operators 栈中弹出一个运算符 op,根据运算符进行计算,并将结果压回 operands 栈。
  2. precedence 方法

    • 返回运算符的优先级,用于比较运算符的优先级。

需要注意的tips:

  1. Java 中的 Stack 类:Java 提供了 java.util.Stack 类,用于实现栈的功能
Stack<Integer> operands = new Stack<>();
Stack<Character> operators = new Stack<>();
  • 字符串遍历:使用 for 循环遍历字符串中的每个字符。
  • 字符判断:使用 Character.isDigit(ch) 判断字符是否为数字。
for (int i = 0; i < expression.length(); i++) {
    char ch = expression.charAt(i);
    if (Character.isDigit(ch)) {
        // 处理数字
    }
}
  1. 边界条件处理
  • 栈为空:在弹出栈元素之前,先检查栈是否为空,以避免 EmptyStackException
  • 多位数处理:当前代码假设所有数字都是个位数,需要处理多位数的情况。
  1. 异常处理
  • 非法表达式:在处理右括号时,如果栈中没有左括号,则表达式非法,可以抛出异常。