青训营刷题-5

2 阅读4分钟

5 简单四则运算解析器

实现一个基本的计算器来计算一个简单的字符串表达式的值。注意事项如下:

  • 输入是一个字符串表达式(可以假设所给定的表达式都是有效的)

  • 字符串表达式可以包含的运算符号为:左括号 (, 右括号 ), 加号 +, 减号 -

  • 可以包含的数字为:非负整数(< 10)

  • 字符串中不包含空格

  • 处理除法 case 的时候,可以直接省略小数部分结果,只保留整数部分参与后续运算

  • 请不要使用内置的库函数 eval

输入格式

如:3+4*5/(3+2)

数据约束

见题目描述

输出格式

计算之后的数字

输入样例

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

输出样例

  • 2
  • 7
  • 12
  • 23

主要思路

  1. 使用栈(Stack)来管理操作数和运算符

    • 操作数栈(Operand Stack) :用于存储数字。
    • 运算符栈(Operator Stack) :用于存储运算符和括号。
  2. 遍历表达式

    • 数字:直接推入操作数栈。

    • 左括号 ( :推入运算符栈,标志着一个新的表达式块的开始。

    • 右括号 ) :弹出运算符栈中的运算符,并执行相应的运算,直到遇到左括号 (

    • 运算符 +, -, *, /

      • 在运算符栈顶有更高或相同优先级的运算符时,先执行栈顶运算符的运算。
      • 然后将当前运算符推入运算符栈。
  3. 处理剩余的运算符

    • 遍历完整个表达式后,弹出运算符栈中剩余的运算符,并执行相应的运算。
  4. 最终结果

    • 操作数栈中剩下的唯一一个数字即为表达式的计算结果。

具体步骤

  1. 初始化

    • 创建两个栈:operandStack 用于存储操作数,operatorStack 用于存储运算符和括号。
    • 定义运算符的优先级:+- 的优先级低于 */
  2. 遍历表达式

    • 对于每个字符 c

      • 如果是数字

        • 将其转换为整数并推入 operandStack
      • 如果是左括号 (

        • 推入 operatorStack
      • 如果是右括号 )

        • 弹出 operatorStack 中的运算符,并执行相应运算,直到遇到左括号 (
      • 如果是运算符 +, -, *, /

        • operatorStack 不为空,且栈顶运算符的优先级大于或等于当前运算符的优先级时,弹出栈顶运算符并执行运算。
        • 将当前运算符推入 operatorStack
  3. 处理剩余的运算符

    • 遍历完表达式后,弹出 operatorStack 中剩余的运算符,并执行相应运算。
  4. 获取结果

    • 操作数栈中最后剩下的数字即为表达式的计算结果。

算法实现

import java.util.*;

public class Main {
    public static int solution(String expression) {
        // Please write your code here
          // 创建操作数栈和运算符栈
          Stack<Integer> operandStack = new Stack<>();
          Stack<Character> operatorStack = new Stack<>();
          
          int n = expression.length();
          for(int i = 0; i < n; i++) {
              char c = expression.charAt(i);
              
              if(Character.isDigit(c)) {
                  // 由于题目中数字都是单个数字(0-9),直接转换并推入操作数栈
                  operandStack.push(c - '0');
              }
              else if(c == '(') {
                  // 左括号,推入运算符栈
                  operatorStack.push(c);
              }
              else if(c == ')') {
                  // 右括号,弹出运算符并执行,直到遇到左括号
                  while(!operatorStack.isEmpty() && operatorStack.peek() != '(') {
                      char op = operatorStack.pop();
                      applyOperator(operandStack, op);
                  }
                  // 弹出左括号 '('
                  if(!operatorStack.isEmpty()) {
                      operatorStack.pop();
                  }
              }
              else if(isOperator(c)) {
                  // 当前字符是运算符
                  while(!operatorStack.isEmpty() && precedence(operatorStack.peek()) >= precedence(c)) {
                      char op = operatorStack.pop();
                      applyOperator(operandStack, op);
                  }
                  // 推入当前运算符
                  operatorStack.push(c);
              }
          }
          
          // 处理剩余的运算符
          while(!operatorStack.isEmpty()) {
              char op = operatorStack.pop();
              applyOperator(operandStack, op);
          }
          
          // 操作数栈中最后的数字就是结果
          return operandStack.pop();
      }
      
      /**
       * 判断字符是否是运算符
       * @param c 输入字符
       * @return 如果是运算符返回 true,否则返回 false
       */
      private static boolean isOperator(char c) {
          return c == '+' || c == '-' || c == '*' || c == '/';
      }
      
      /**
       * 获取运算符的优先级
       * @param op 运算符
       * @return 优先级数值,数值越大优先级越高
       */
      private static int precedence(char op) {
          if(op == '+' || op == '-') {
              return 1;
          }
          else if(op == '*' || op == '/') {
              return 2;
          }
          return 0;
      }
      
      /**
       * 执行运算符的运算,并将结果推回操作数栈
       * @param operandStack 操作数栈
       * @param operator 当前运算符
       */
      private static void applyOperator(Stack<Integer> operandStack, char operator) {
          // 弹出两个操作数
          int b = operandStack.pop();
          int a = operandStack.pop();
          int result = 0;
          
          switch(operator) {
              case '+':
                  result = a + b;
                  break;
              case '-':
                  result = a - b;
                  break;
              case '*':
                  result = a * b;
                  break;
              case '/':
                  // 整数除法,自动舍弃小数部分
                  result = a / b;
                  break;
          }
          // 将结果推回操作数栈
          operandStack.push(result);
      }

    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);
    }
}