华为 OD 训练营 · 题解精讲
LC224. 基本计算器
LC224. 基本计算器 预习和复习可以查看我的讲解视频。
题目描述
给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。 示例 1: 输入:s = "1 + 1" 输出:2 示例 2: 输入:s = " 2-1 + 2 " 输出:3 示例 3: 输入:s = "(1+(4+5+2)-3)+(6+8)" 输出:23 提示: 1 <= s.length <= 3 * 105 s 由数字、'+'、'-'、'('、')'、和 ' ' 组成 s 表示一个有效的表达式
题目解析
解题思路
本题要求计算包含加减和括号的表达式,核心思路是将减法统一为加法(加一个负数),并利用栈处理括号带来的优先级问题。遍历字符串时,维护当前符号 sign(1 表示正,-1 表示负)和当前括号内的累加结果 res。遇到数字时,将数字乘以当前符号累加到 res;遇到 ( 时,将当前 res 和 sign 压栈,然后重置 res 和 sign 以计算括号内的值;遇到 ) 时,从栈中弹出括号外的符号和结果,将当前括号结果乘以符号后与栈中结果相加。
关键步骤
1. 处理数字:连续数字字符组成完整整数,计算其值后乘以当前符号 sign 累加到 res。 2. 处理 `+` / `-`:更新 sign 为 1 或 -1,表示后续数字的符号。 3. 处理 `(`:将当前 res 和 sign 依次压栈,然后重置 res = 0、sign = 1,开始计算括号内表达式。 4. 处理 `)`:弹出栈顶的 formerSign 和 formerRes,将当前括号结果 res 乘以 formerSign 后与 formerRes 相加,得到括号外的结果。
示例:计算 "1-(2+3)"
初始 res=0, sign=1
遇到 1 → res = 0 + 1*1 = 1
遇到 - → sign = -1
遇到 ( → 压栈 res=1, sign=-1;重置 res=0, sign=1
遇到 2 → res = 0 + 2*1 = 2
遇到 + → sign = 1
遇到 3 → res = 2 + 3*1 = 5
遇到 ) → 弹出 formerSign=-1, formerRes=1 → res = 1 + (-1)*5 = -4复杂度分析
- 时间复杂度:O(n),其中 n 为字符串长度,每个字符只处理一次。
- 空间复杂度:O(n),栈在最坏情况下存储所有括号层级的中间结果和符号。
参考代码
# 登录 AlgoMooc 官网获取更多算法图解
# https://www.algomooc.com
# 作者:程序员吴师兄
# 代码有看不懂的地方一定要私聊咨询吴师兄呀
# 基本计算器( LeetCode 224 ):https://leetcode-cn.com/problems/basic-calculator
class Solution:
def calculate(self, s: str) -> int:
# 使用栈来储存字符串表达式中的数字
stack = list()
# 为了方便计算,所有的操作都视为加法操作
# 那么原先的减法操作就相当于是加一个负数
# 默认都是正数
sign = 1
# 保存计算的结果
res = 0
# 获取字符串的长度,然后获取里面的每个字符
length = len(s)
# 从 0 开始访问字符串中的每个字符
i = 0
# 获取字符串里面的每个字符
while i < length:
# 获取此时的字符
ch = s[i]
if ch == ' ' :
i += 1
# 如果当前字符是数字的话
elif ch.isdigit() :
# 那么把获取到的数累加到结果 res 上
value = ord(s[i]) - ord('0')
# 去查看当前字符的后一位是否存在
# 如果操作并且后一位依旧是数字,那么就需要把后面的数字累加上来
while i + 1 < length and s[i + 1].isdigit():
i += 1
value = value * 10 + ord(s[i]) - ord('0')
# 那么把获取到的数累加到结果 res 上
res += value * sign
i += 1
# 如果是 '+'
elif ch == '+' :
# 那么说明直接加一个正数
sign = 1
i += 1
# 如果是 '-'
elif ch == '-' :
# 那么说明加一个负数
sign = -1
i += 1
# 如果是 '('
elif ch == '(' :
# 根据数学计算规则,需要先计算括号里面的数字
# 而什么时候计算呢?
# 遇到 ) 为止
# 所以,在遇到 ) 之前需要把之前计算好的结果存储起来再计算
# ( ) 直接的计算规则和一开始是一样的
# 1、先把 ( 之前的结果存放到栈中
stack.append(res)
# 2、重新初始化 res 为 0
res = 0
# 3、把 ( 左边的操作符号存放到栈中
# 比如如果是 5 - ( 2 + 3 ) ,那么就是把 -1 存放进去
# 比如如果是 5 +( 2 + 3 ) ,那么就是把 1 存放进去
stack.append(sign)
# 4、为了方便计算,所有的操作都视为加法操作
# 那么原先的减法操作就相当于是加一个负数
# 默认都是正数
sign = 1
i += 1
# 如果是 ')'
elif ch == ')' :
# 那么就需要把栈中存放的元素取出来了
# 在 ch == '(' 这个判断语句中,每次都会往栈中存放两个元素
# 1、先存放左括号外面的结果
# 2、再存放左括号外面的符号
# 1、先获取栈顶元素,即左括号外面的符号,查看是 + 还是 -
# 把栈顶元素弹出
formerSign = stack.pop()
# 2、再获取栈顶元素,即左括号结果
# 把栈顶元素弹出
formerRes = stack.pop()
# 那结果就是括号外面的结果 + 括号里面的结果,至于是加正数还是负数,取决于左括号外面的符号
res = formerRes + formerSign * res
i += 1
# 返回计算好的结果
return res
四、复杂度分析
时间复杂度:O(n),其中 n 为字符串 s 的长度。需要遍历字符串 s 一次,计算表达式的值。
空间复杂度:O(n),其中 n 为字符串 s 的长度。空间复杂度主要取决于栈的空间,栈中的元素数量不超过 n。
五、直接调API解法
如果上述内容实在学得吃力,但机试中又不幸遇到。可以直接使用内置函数eval()解题。内置函数eval()能够直接计算一个字符串表达式的值,能够识别加减乘除以及括号。
s = input()
print(eval(s))