Given a string representing a mathematical expression containing integers, parentheses, addition, and subtraction operators, evaluate and return the result of the expression.
Input: s = '18-(7+(2-4))'
Output: 13
New to this one? Start here. Below is the core idea in everyday language, plus a real-world analogy—then the sections that follow build on it with the formal reasoning, code, and step-by-step walkthroughs.
You’re adding up a bill when a sub-total in brackets interrupts you. You jot down where you were (your running total and whether the bracket is being added or subtracted), handle the bracket from scratch, then pick your notes back up and fold the bracket’s value into your total. That “save my place, come back to it” is a stack — and it’s exactly how parentheses nest.
At first, it might seem overwhelming to deal with expressions that include a variety of elements like negative numbers, nested expressions inside parentheses, and numbers with multiple digits. The key to managing this complexity is to break down the problem into smaller, more manageable parts. Let's first focus on evaluating simple expressions that contain no parentheses.
Handling positive and negative signs
Consider the following expression:
There's already some complexity in this expression with there being two signs to consider: plus and minus. An immediate simplification we can make is to treat all expressions as ones of pure addition. This is possible when we assign signs to each number (1 representing + and -1 representing -). This sign can be multiplied by the number to attain its correct value. This allows us to just focus on performing additions:
Processing numbers with multiple digits
Another complexity in this expression is that some numbers have multiple digits. We’ll need a way to build numbers digit by digit until we reach the end of the number. We can build a number using the variable curr_num, which is initially set to 0. Every time we encounter a new digit, we multiply curr_num by 10 and add the new digit to it, effectively shifting all digits to the left and appending the new digit.
We can see this process play out for the string “123” in the following illustration:
We can stop building this number once we encounter a non-digit character, indicating the end of the number.
Evaluating an expression without parentheses
With the information from the section above, let's evaluate the following expression, which contains no parentheses. We’ll start off with a sign of 1:
Upon reaching the ‘-’ operator, we’ve reached the end of the first number (28). So, let’s:
curr_num to 0 before building the next number.Once we reach the second operator, we multiply the current number (10) by its sign of -1 before adding the resulting product (-10) to the result. This effectively subtracts 10 from the result:
Finally, once we’ve reached the end of the string, we just add the final number (7) to the result after multiplying it by its sign of 1:
Evaluating expressions containing parentheses
Now that we can solve simple expressions, it's time to bring parentheses into the discussion. Moving forward, we define a nested expression as one that’s inside a pair of parentheses.
One challenge is that we need to evaluate the results of nested expressions before we can calculate the original expression. Once all nested expressions are evaluated, we can evaluate the original expression.
Consider another problem in this chapter that also contains parentheses: Valid Parenthesis Expression. In that problem, we used a stack to process nested parentheses in the right order. This suggests a stack might also help us evaluate nested expressions in the right order. Let’s explore this idea further.
Similar to Valid Parenthesis Expression, an opening parenthesis ‘(‘ indicates the start of a new nested expression, whereas a closing parenthesis ‘)’ indicates the end of one. Understanding this, let’s try to use a stack to solve the following expression.
We already know how to evaluate expressions without parentheses, so let’s just focus on what to do when we encounter a parenthesis.
At the first opening parenthesis, we know a nested expression has started. Before we evaluate the nested expression, we’ll need to save the running result (res) of the current expression, as well as the sign of this upcoming nested expression. This way, once we’re done evaluating the nested expression, we can resume where we were in the current expression.
Here are the steps for when we encounter an opening parenthesis:
stack.push(res): Save the running result on the stack.
stack.push(sign): Save the sign of the upcoming nested expression on the stack.
res = 0, sign = 1: Reset these variables because we’re about to begin calculating a new expression.

The next parenthesis we encounter is an opening parenthesis. Again, this indicates the start of a new nested expression. Let’s save the current result and sign on the stack, before resetting them to evaluate the upcoming nested expression:

The next parenthesis we encounter is a closing parenthesis. This means the current nested expression just ended, and we need to merge its result with the outer expression. Here’s how we do this:
res *= stack.pop(): Apply the sign of the current nested expression to its result.
res += stack.pop(): Add the result of the outer expression to the result of the current nested expression.
After applying those operations, the value of res will be 5, representing the result of the highlighted part of the expression below:
At the final closing parenthesis, we can apply the same steps:
Finally, the value of res will be 13, representing the result of the entire expression:
Now that we’ve reached the end of the string, we can return res.
def evaluate_expression(s: str) -> int:
stack = []
curr_num, sign, res = 0, 1, 0
for c in s:
if c.isdigit():
curr_num = curr_num * 10 + int(c)
# If the current character is an operator, add 'curr_num' to the result
# after multiplying it by its sign.
elif c == '+' or c == '-':
res += curr_num * sign
# Update the sign and reset 'curr_num'.
sign = -1 if c == '-' else 1
curr_num = 0
# If the current character is an opening parenthesis, a new nested expression
# is starting.
elif c == '(':
# Save the current 'res' and 'sign' values by pushing them onto
# the stack, then reset their values to start calculating the new nested
# expression.
stack.append(res)
stack.append(sign)
res, sign = 0, 1
# If the current character is a closing parenthesis, a nested expression has
# ended.
elif c == ')':
# Finalize the result of the current nested expression.
res += sign * curr_num
# Apply the sign of the current nested expression’s result before adding
# this result to the result of the outer expression.
res *= stack.pop()
res += stack.pop()
curr_num = 0
# Finalize the result of the overall expression.
return res + curr_num * signlong long evaluateExpression(string s) {
vector<long long> stack;
long long num = 0;
long long sign = 1;
long long result = 0;
for (char c : s) {
if (isdigit(c)) {
num = num * 10 + (c - '0'); // build a multi-digit number
} else if (c == '+' || c == '-') {
result += sign * num; // settle the number just finished
num = 0;
if (c == '+') {
sign = 1;
} else {
sign = -1;
}
} else if (c == '(') {
stack.push_back(result); // save the outside context
stack.push_back(sign);
result = 0;
sign = 1;
} else if (c == ')') {
result += sign * num; // settle the last number inside
num = 0;
result *= stack.back(); // multiply by the sign before '('
stack.pop_back();
result += stack.back(); // add the outside result
stack.pop_back();
}
}
return result + sign * num; // settle any trailing number
}public long evaluateExpression(String s) {
Deque<Long> stack = new ArrayDeque<>();
long num = 0, sign = 1, result = 0;
for (char c : s.toCharArray()) {
if (Character.isDigit(c)) {
num = num * 10 + (c - '0'); // build a multi-digit number
} else if (c == '+' || c == '-') {
result += sign * num; // settle the number just finished
num = 0;
sign = (c == '+') ? 1 : -1;
} else if (c == '(') {
stack.push(result); // save the outside context
stack.push(sign);
result = 0; sign = 1;
} else if (c == ')') {
result += sign * num; // settle the last number inside
num = 0;
result *= stack.pop(); // sign before '('
result += stack.pop(); // outside result
}
}
return result + sign * num; // settle any trailing number
}Time complexity: The time complexity of evaluate_expression is because we traverse each character of the expression once, processing nested expressions using the stack, where each stack push or pop operation takes time.
Space complexity: The space complexity is because the stack can grow proportionally to the length of the expression.
Two step-by-step walkthroughs of the algorithm below: a valid case (the expected happy path) and an invalid / edge case that exercises the tricky parts. Each step shows the program state as the code runs—the same steps apply to Python, C++ and Java.
"6-(3-1)". Expected: 6 − (3−1) = 4.saved context (result, sign) restored on ')'
(result=6, sign=−1) and start the bracket fresh.
3−1 = 2. Pop sign (−1): 2 × −1 = −2. Pop result (6): 6 + (−2) = 4. Stack empty.
| read | what we do | num | sign | result | stack |
|---|---|---|---|---|---|
| — | initialize | 0 | +1 | 0 | (empty) |
| 6 | build number | 6 | +1 | 0 | (empty) |
| - | settle 6; sign becomes − | 0 | −1 | 6 | (empty) |
| ( | save (6, −1); reset | 0 | +1 | 0 | 6, −1 |
| 3 | build number | 3 | +1 | 0 | 6, −1 |
| - | settle 3; sign becomes − | 0 | −1 | 3 | 6, −1 |
| 1 | build number | 1 | −1 | 3 | 6, −1 |
| ) | settle→2; ×(−1)=−2; +6 | 0 | −1 | 4 | (empty) |
| end | result += sign·num (num=0) | 0 | −1 | 4 | (empty) |
"-(3-(2-1))". Expected: −(3 − (2−1)) = −(3−1) = −2.saved context (result, sign) restored on ')'
(0, −1) then inner (3, −1).
2−1=1; ×(−1)=−1; +3 → 2. Second ')': ×(−1)=−2; +0 → −2.
| read | what we do | num | sign | result | stack |
|---|---|---|---|---|---|
| - | settle 0; sign − | 0 | −1 | 0 | (empty) |
| ( | save (0, −1); reset | 0 | +1 | 0 | 0, −1 |
| 3 | build | 3 | +1 | 0 | 0, −1 |
| - | settle 3; sign − | 0 | −1 | 3 | 0, −1 |
| ( | save (3, −1); reset | 0 | +1 | 0 | 0, −1, 3, −1 |
| 2 | build | 2 | +1 | 0 | 0, −1, 3, −1 |
| - | settle 2; sign − | 0 | −1 | 2 | 0, −1, 3, −1 |
| 1 | build | 1 | −1 | 2 | 0, −1, 3, −1 |
| ) | 2−1=1; ×(−1)=−1; +3 | 0 | −1 | 2 | 0, −1 |
| ) | ×(−1)=−2; +0 | 0 | −1 | −2 | (empty) |
| end | num=0, nothing to add | 0 | −1 | −2 | (empty) |
Notice how the stack grew to four entries (two contexts) at the deepest point, then unwound perfectly — one pop-pair per closing bracket.
c.isdigit() detects digits; the list holds the saved result then sign, popped in reverse. Python ints never overflow.
Same steps. We use long long and a vector<long long> so big sub-totals can’t overflow; c - '0' turns a digit character into its value.
Same steps with long and a Deque<Long>. stack.pop() auto-unboxes to long for the arithmetic.