Before attempting this problem, you should be comfortable with:
This problem adds parentheses to the basic calculator, which introduces nested sub-expressions. We handle this by using a stack that can hold both numbers and operators. When we see an opening parenthesis, we save the current operator and reset our state. When we see a closing parenthesis, we evaluate everything inside, then retrieve the saved operator to continue. The stack essentially lets us pause the outer expression, fully evaluate the inner one, and resume.
@ to the string to trigger final processing.(, push the previous operator onto the stack and reset the operator to +.) or @):* and / by popping and computing immediately).), sum all numbers on the stack until we hit a saved operator, then use that operator for the combined result.class Solution:
def calculate(self, s: str) -> int:
def evaluate(x, y, operator):
if operator == "+":
return x
if operator == "-":
return -x
if operator == "*":
return x * y
return int(x / y)
stack = []
curr = 0
previous_operator = "+"
s += "@"
for c in s:
if c.isdigit():
curr = curr * 10 + int(c)
elif c == "(":
stack.append(previous_operator)
previous_operator = "+"
else:
if previous_operator in "*/":
stack.append(evaluate(stack.pop(), curr, previous_operator))
else:
stack.append(evaluate(curr, 0, previous_operator))
curr = 0
previous_operator = c
if c == ")":
while type(stack[-1]) == int:
curr += stack.pop()
previous_operator = stack.pop()
return sum(stack)Where is the length of the expression
Parentheses naturally suggest recursion. When we encounter an opening parenthesis, we can recursively evaluate everything inside it and treat the result as a single number. This makes the problem cleaner since each recursive call handles one level of nesting. The base case is a simple expression without parentheses, which we evaluate using the same logic as Basic Calculator II.
@ to the string. Use an index variable shared across recursive calls.solve function:+).(, increment the index and recursively call solve to get the inner result.+/-, compute for *//).), break out of the loop.solve starting from index 0 and return its result.class Solution:
def calculate(self, s: str) -> int:
def evaluate(x, y, operator):
if operator == "+":
return x
if operator == "-":
return -x
if operator == "*":
return x * y
return int(x / y)
def solve(i):
stack = []
curr = 0
previous_operator = "+"
while i[0] < len(s):
c = s[i[0]]
if c == "(":
i[0] += 1
curr = solve(i)
elif c.isdigit():
curr = curr * 10 + int(c)
else:
if previous_operator in "*/":
stack.append(evaluate(stack.pop(), curr, previous_operator))
else:
stack.append(evaluate(curr, 0, previous_operator))
if c == ")":
break
curr = 0
previous_operator = c
i[0] += 1
return sum(stack)
s += "@"
return solve([0])Where is the length of the expression
When encountering an opening parenthesis, you must save the current operator and reset to a default state (usually +). Failing to reset causes the operator before the parenthesis to be applied incorrectly inside.
# Wrong: not resetting previous_operator when entering parenthesis
# Correct: save current operator to stack, reset to '+'When a closing parenthesis is encountered, you must sum all values inside the parentheses and then apply the saved operator from before the opening parenthesis. Missing either step produces wrong results.
Multiplication and division still have higher precedence than addition and subtraction, even inside parentheses. The parentheses only group sub-expressions; they do not change the relative precedence of operators within them.
When using recursion to handle parentheses, the index must be shared across recursive calls (e.g., using a mutable reference or array). Failing to properly advance the index after processing a sub-expression causes characters to be processed multiple times or skipped.
Many solutions append a sentinel character (like @) to trigger processing of the final number. Without this, the last number or sub-expression may not be evaluated.