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

169 阅读3分钟

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


问题描述

小F需要实现一个基本的计算器,能够计算包含数字、运算符+-*/和括号()的字符串表达式的值。具体要求如下:

  1. 输入

    • 一个有效的字符串表达式。
    • 可能包含正整数(0-9)、运算符 +-*/ 及括号 ()
    • 字符串中没有空格。
  2. 输出

    • 表达式的计算结果。
    • 除法结果保留整数部分(类似于地板除 // 的效果)。
  3. 限制

    • 不能使用 Python 的内置函数 eval

解题思路

本题要求实现一个解析器,处理表达式中的数字和运算符,同时考虑运算优先级和括号的影响。以下是完整的解决方案步骤:


算法设计

  1. 核心概念

    • 运算符优先级

      • +- 的优先级最低。
      • */ 的优先级较高。
      • 括号 () 可以改变默认的运算顺序。
    • 栈的应用

      • 使用 数字栈 存储操作数。
      • 使用 运算符栈 存储运算符,帮助处理优先级。
    • 计算规则

      • 碰到低优先级的运算符时,先处理栈顶的高优先级运算符。
      • 碰到右括号 ) 时,处理括号内的所有运算。
      • 表达式解析完毕后,清空运算符栈。
  2. 处理流程

    • 遍历表达式的每个字符:

      • 如果是数字,解析完整的数字并压入 数字栈
      • 如果是运算符,按优先级与 运算符栈 中的运算符进行比较,处理优先级较高的运算符。
      • 如果是左括号 (,直接压入 运算符栈
      • 如果是右括号 ),处理栈中的运算直到遇到左括号。
    • 遍历完成后,处理 运算符栈 中剩余的运算。

  3. 辅助函数

    • apply_operation(op, b, a):执行单次运算,返回结果。
    • precedence(op):返回运算符的优先级。

代码实现

def calculate(expression: str) -> int:
    # 运算符优先级
    precedence = {'+': 1, '-': 1, '*': 2, '/': 2}

    # 应用运算符的操作
    def apply_operation(op, b, a):
        if op == '+':
            return a + b
        elif op == '-':
            return a - b
        elif op == '*':
            return a * b
        elif op == '/':
            return a // b  # 整数除法
        raise ValueError("Invalid operator")

    # 栈定义
    numbers = []  # 数字栈
    ops = []      # 运算符栈

    i = 0
    while i < len(expression):
        char = expression[i]

        if char.isdigit():
            # 解析完整的数字(支持多位数)
            num = 0
            while i < len(expression) and expression[i].isdigit():
                num = num * 10 + int(expression[i])
                i += 1
            numbers.append(num)
            continue  # 已经移动了指针,跳过 i 的递增

        elif char in precedence:
            # 处理当前运算符与栈中运算符的优先级
            while ops and ops[-1] != '(' and precedence[char] <= precedence[ops[-1]]:
                numbers.append(apply_operation(ops.pop(), numbers.pop(), numbers.pop()))
            ops.append(char)

        elif char == '(':
            # 左括号直接入栈
            ops.append(char)

        elif char == ')':
            # 右括号:处理括号内的运算
            while ops and ops[-1] != '(':
                numbers.append(apply_operation(ops.pop(), numbers.pop(), numbers.pop()))
            ops.pop()  # 弹出左括号

        i += 1

    # 处理剩余运算符
    while ops:
        numbers.append(apply_operation(ops.pop(), numbers.pop(), numbers.pop()))

    # 最终结果
    return numbers[0]

# 测试样例
if __name__ == '__main__':
    assert calculate("1+2*3") == 7
    assert calculate("(1+(4+5+2)-3)+(6+8)") == 23
    assert calculate("3+2*2") == 7
    assert calculate("3/2") == 1
    assert calculate("3+5/2") == 5

测试样例

  • 样例 1

    calculate("1+2*3")
    
    • 运算步骤:

      1. 解析:数字 1,压入数字栈。
      2. 解析:+,压入运算符栈。
      3. 解析:数字 2,压入数字栈。
      4. 解析:*,压入运算符栈。
      5. 解析:数字 3,压入数字栈。
      6. 处理高优先级的 * 运算:结果 6 压入数字栈。
      7. 处理低优先级的 + 运算:结果 7
    • 输出7

  • 样例 2

    calculate("(1+(4+5+2)-3)+(6+8)")
    
    • 运算步骤:

      1. 处理括号中的表达式。
      2. 按运算优先级计算子表达式。
    • 输出23


总结

  1. 数据结构

    • 使用栈来处理括号和运算符优先级。
    • 数字栈存储操作数,运算符栈存储操作符。
  2. 时间复杂度

    • 遍历表达式:O(n)。
    • 处理运算符栈:每个运算符最多入栈出栈一次,复杂度 O(n)。
    • 总复杂度:O(n)。
  3. 优点

    • 支持多位数字和嵌套括号。
    • 遵循运算优先级规则。
    • 不依赖 eval,逻辑清晰、易扩展。