简单四则运算解析器

81 阅读4分钟

算法设计核心

这个问题属于表达式解析的典型应用,需要处理操作符优先级、括号嵌套以及多种运算类型(加、减、乘、除)。实现的重点包括以下几点:

  1. 优先级的设计

    • 在常见的数学表达式中,括号优先级最高,接下来是乘法和除法,最后是加法和减法。
    • 在解析过程中,我们使用 precedence(op) 函数来确定当前操作符的优先级,从而决定是将操作符压入栈还是弹出并计算栈顶的表达式。
  2. 两栈法的设计

    • 操作数栈(values :用于存储当前解析到的数字。
    • 操作符栈(ops :用于存储解析到的运算符以及括号。
    • 每当需要计算时,从这两个栈中弹出对应的值和操作符,完成一次计算后将结果压回 values 栈。
  3. 括号的处理

    • 左括号 ( 表示一个新的优先级范围,当遇到右括号 ) 时,弹出并计算所有操作,直到遇到对应的左括号 (
    • 这部分处理是为了确保括号内的表达式优先计算。
  4. 数字解析

    • 输入字符串可能包含多位数(例如 123),因此需要一个循环解析完整的数字,而不仅仅是单个字符。

具体实现说明

代码的实现严格按照上述逻辑展开,每一部分都经过精心设计以确保正确性和效率:

  1. 优先级函数 precedence(op)

    • 通过简单的条件判断返回操作符的优先级,支持动态扩展。如果将来需要支持其他操作符(如幂运算 ^),只需要在这里添加对应规则。
  2. 运算函数 apply_op(a, b, op)

    • 该函数对两个操作数 ab 应用操作符 op,实现加减乘除功能。
    • 除法部分采用整除运算 //,符合题目要求。
  3. 栈顶计算 calculate()

    • 这是核心的计算函数,负责从 valuesops 栈中弹出数据并完成一次运算,将结果重新压入 values 栈。
  4. 主循环的逻辑

    • 遍历输入字符串时,根据当前字符的类型(数字、操作符、括号)分别处理:

      • 如果是数字,解析完整的数字并压入 values 栈。
      • 如果是操作符,比较优先级并决定是压栈还是弹栈计算。
      • 如果是括号,按照括号的规则处理。
  5. 最终计算

    • 当字符串遍历结束后,ops 栈中可能还存在未处理的操作符(例如 1+2+3),需要继续弹栈计算,直到所有操作完成。

代码的扩展性

这个实现非常灵活,可以扩展支持更多功能:

  1. 支持更多操作符

    • 例如幂运算(^),可以在 precedenceapply_op 中添加对应规则。
    • 新增操作符时,只需确保优先级规则正确,主循环无需修改。
  2. 处理浮点数

    • 当前实现中,所有运算都是整数运算。如果需要支持小数,可以将数字解析为浮点数,并将整除运算 // 替换为正常除法 /
  3. 空格处理

    • 如果输入字符串可能包含空格,可以在遍历时跳过空格,或预先将字符串中的空格移除。
  4. 错误检测

    • 如果输入表达式无效(如括号不匹配,或包含非法字符),可以在主循环中添加检查逻辑,抛出异常或返回错误信息。

时间和空间复杂度

  1. 时间复杂度

    • 每个字符最多进栈和出栈一次,因此时间复杂度为 O(n)O(n),其中 nn 是输入字符串的长度。
    • 复杂操作(如计算括号内的表达式)受益于栈的结构,避免了多次重复计算。
  2. 空间复杂度

    • 空间复杂度为 O(n)O(n),主要由栈的深度决定。在最坏情况下(例如大量嵌套括号),栈的最大深度为字符串长度 nn。

样例详解

样例 1:1+1

  • 遍历字符:

    • 解析 1,压入 values 栈。
    • 遇到 +,压入 ops 栈。
    • 解析第二个 1,压入 values 栈。
  • 弹栈计算:1 + 1 = 2,结果为 2

样例 2:3+4*5/(3+2)

  • 按优先级计算:

    • 先计算括号内的 (3+2),结果为 5
    • 再计算乘除部分 4*5/5,结果为 4
    • 最后计算加法 3 + 4,结果为 7

样例 5:2*(5+5*2)/3+(6+8*3)

  • 按括号和优先级解析:

    • 括号内计算 5 + 5*2,结果为 15
    • 外部计算乘法和除法:2*15/3,结果为 10
    • 计算加法和乘法:10 + 6 + 8*3,结果为 40

通过以上解析和实现,代码实现了高效、正确的表达式求值功能,同时提供了良好的扩展性和稳定性。这种方法可以应用于更复杂的表达式解析问题,适用于多种场景。