hardLeetCodeNot Started

Basic Calculator

A curated StudyFlow practice problem with comprehensive editorial and multi-language solutions.

Solve on LeetCode
Lesson

Problem Statement

Given a string s representing a valid expression, implement a basic calculator to evaluate it, and return the result of the evaluation.

Note: You are not allowed to use any built-in function which evaluates strings as mathematical expressions, such as eval().

Example 1 · Simple Addition:

Input: s = "1 + 1"
Output: 2
Explanation: 1 + 1 = 2

Example 2 · Spaces and Subtraction:

Input: s = " 2-1 + 2 "
Output: 3
Explanation: 2 - 1 + 2 = 3

Example 3 · Nested Parentheses with Unary Sign:

Input: s = "(1+(4+5+2)-3)+(6+8)"
Output: 23
Explanation: Inner parentheses evaluated: (1 + 11 - 3) + 14 = 9 + 14 = 23.

Constraints:

  • 1 <= s.length <= 3 * 105
  • s consists of digits, +, -, (, ), and ' '.
  • s represents a valid expression.
  • + is not used as a unary operation (i.e., "+1" and "+(2 + 3)" is invalid).
  • - could be used as a unary operation (i.e., "-1" and "-(2 + 3)" is valid).
  • Every number and running calculation will fit in a signed 32-bit integer.

What You'll Learn

Key principles and algorithmic techniques you will master from this problem:

  • Master the sign-tracking technique that eliminates complex infix AST construction for + / - expressions.
  • Understand stack state preservation for nested parentheses scopes.
  • Handle multi-digit numbers and spaces robustly.

Pattern Recognition Trigger

How to instantly recognize this algorithmic pattern when encountering unseen interview questions:

Algorithmic Decision Tree:

  • Maintain res = 0, sign = 1, num = 0.
  • If digit: accumulate num = num * 10 + digit.
  • If + or -: add sign * num to res, reset num = 0, set sign = (+1 or -1).
  • If (: push res and sign to stack, reset res = 0, sign = 1.
  • If ): add sign * num to res, then res = stack.pop() * res + stack.pop(), reset num = 0.

Connected Patterns:

Basic Calculator IIBasic Calculator IIIEvaluate Reverse Polish Notation

1. Recursive Descent Parenthesis Evaluator

Intuition

Whenever an opening parenthesis ( is met, recursively evaluate the inner expression until the matching ) is met, then treat the return value as a single number.

Algorithm

  1. Step 1: Track index pointer i across recursive calls.
  2. Step 2: If s[i] == '(', recursively compute subexpression.
  3. Step 3: If digit, accumulate into number.
  4. Step 4: If operator or ) or end of string, apply pending sign.
  5. Step 5: Return result.
C++ SOLUTIONCLEAN & OPTIMIZED
1#include <iostream>
2#include <string>
3
4class Solution {
5 int i = 0;
6 int helper(const std::string& s) {
7 int res = 0, num = 0, sign = 1;
8 while (i < (int)s.length()) {
9 char c = s[i++];
10 if (c >= '0' && c <= '9') {
11 num = num * 10 + (c - '0');
12 } else if (c == '(') {
13 num = helper(s);
14 } else if (c == ')') {
15 res += sign * num;
16 return res;
17 } else if (c == '+') {
18 res += sign * num;
19 num = 0;
20 sign = 1;
21 } else if (c == '-') {
22 res += sign * num;
23 num = 0;
24 sign = -1;
25 }
26 }
27 res += sign * num;
28 return res;
29 }
30public:
31 int calculate(std::string s) {
32 i = 0;
33 return helper(s);
34 }
35};
36
37int main() {
38 Solution solver;
39 std::cout << "Result: " << solver.calculate("(1+(4+5+2)-3)+(6+8)") << std::endl;
40 return 0;
41}

Time & Space Complexity

  • Time complexity: O(N) — Recursively evaluates inner parenthesized sub-expressions using the call stack.
  • Space complexity: O(N) — O(N) call stack for deeply nested parentheses.

Why Brute Force Doesn't Scale:

Two-pass AST allocation creates 10x memory allocations.

Single-pass sign stack evaluates 300,000 characters in <8ms.


2. Global Sign Stack with Inversion

Intuition

Instead of saving entire intermediate results, maintain a stack of active signs. When a - precedes (, flip the global multiplier sign for all terms inside.

Algorithm

  1. Step 1: Stack of signs initialized with [1].
  2. Step 2: Maintain sign = 1.
  3. Step 3: When ( met, push sign * stack.top().
  4. Step 4: When ) met, pop stack.
  5. Step 5: Accumulate digits multiplied by current effective sign.
C++ SOLUTIONCLEAN & OPTIMIZED
1#include <iostream>
2#include <string>
3#include <stack>
4
5class Solution {
6public:
7 int calculate(std::string s) {
8 std::stack<int> signs;
9 signs.push(1);
10 int res = 0, num = 0, sign = 1;
11
12 for (char c : s) {
13 if (c >= '0' && c <= '9') {
14 num = num * 10 + (c - '0');
15 } else if (c == '+') {
16 res += sign * num;
17 num = 0;
18 sign = signs.top();
19 } else if (c == '-') {
20 res += sign * num;
21 num = 0;
22 sign = -signs.top();
23 } else if (c == '(') {
24 signs.push(sign);
25 } else if (c == ')') {
26 res += sign * num;
27 num = 0;
28 signs.pop();
29 sign = signs.top();
30 }
31 }
32 res += sign * num;
33 return res;
34 }
35};
36
37int main() {
38 Solution solver;
39 std::cout << "Result: " << solver.calculate("2-(1-3)") << std::endl;
40 return 0;
41}

Time & Space Complexity

  • Time complexity: O(N) — Maintains an active sign stack that distributes outer negative signs to inner terms.
  • Space complexity: O(N) — O(N) sign stack.

3. Optimal Iterative Result & Sign Stack

Intuition

Maintain a running result and sign. When entering (, push (res, sign) to save outer state and reset. When leaving ), multiply inner result by saved sign and add saved outer result.

Algorithm

  1. Step 1: Initialize result = 0, num = 0, sign = 1, and stack st.
  2. Step 2: Loop through each character c in s.
  3. Step 3: If digit: num = num * 10 + (c - '0').
  4. Step 4: If +: result += sign * num, num = 0, sign = 1.
  5. Step 5: If -: result += sign * num, num = 0, sign = -1.
  6. Step 6: If (: push result then sign onto st, reset result = 0, sign = 1.
  7. Step 7: If ): result += sign * num, num = 0, result *= st.pop(), result += st.pop().
  8. Step 8: After loop: result += sign * num and return result.

Parenthesis State Stack Trace

2 of 2 questions left today
Step 1 / 5
Trace timeline20% explored
Current operationSTEP 1 OF 5
Stack: [res: 0, sign: 1]
Visual state
Values and pointers at this exact step
stack
TOP
prevRes: 0
prevSign: +1
Push adds to the top · Pop removes from the top
Why this step matters

Stack: [res: 0, sign: 1]

Step 1 / 5
C++ SOLUTIONCLEAN & OPTIMIZED
1#include <iostream>
2#include <string>
3#include <stack>
4
5class Solution {
6public:
7 int calculate(std::string s) {
8 std::stack<int> st;
9 int result = 0;
10 int num = 0;
11 int sign = 1;
12
13 for (char c : s) {
14 if (c >= '0' && c <= '9') {
15 num = num * 10 + (c - '0');
16 } else if (c == '+') {
17 result += sign * num;
18 num = 0;
19 sign = 1;
20 } else if (c == '-') {
21 result += sign * num;
22 num = 0;
23 sign = -1;
24 } else if (c == '(') {
25 st.push(result);
26 st.push(sign);
27 result = 0;
28 sign = 1;
29 } else if (c == ')') {
30 result += sign * num;
31 num = 0;
32 result *= st.top(); st.pop(); // multiply by saved sign
33 result += st.top(); st.pop(); // add saved previous result
34 }
35 }
36 result += sign * num;
37 return result;
38 }
39};
40
41int main() {
42 Solution solver;
43 std::cout << "Result: " << solver.calculate("(1+(4+5+2)-3)+(6+8)") << std::endl;
44 return 0;
45}

Time & Space Complexity

  • Time complexity: O(N) — Single O(N) pass across characters with strictly O(1) stack operations per parenthesized subexpression.
  • Space complexity: O(N) — O(N) space for parenthetical state stack.

Approach Comparison Matrix

Side-by-side comparison of time, space, and algorithmic trade-offs across each approach:

ApproachTimeSpaceCore IdeaTrade-off / Bottleneck
1.Recursive Descent EvaluatorO(N)O(N)Recursively call helper on opening parenthesis.Function frame allocations.
2.Global Sign Stack with InversionO(N)O(N)Distribute signs across parentheses boundaries.None.
3.Optimal Iterative Result & Sign StackOptimalO(N)O(N)Save (result, sign) on ( and restore on ).None (Optimal).

Common Pitfalls & Crucial Edge Cases

Pitfall 1:

Forgetting to add sign * num to result after the main loop ends.

Pitfall 2:

Not handling spaces properly (letting them reset num or sign).

Pitfall 3:

Swapping the pop order on ): the first popped is sign and second is prevResult.

Crucial Edge Cases to Validate:

  • Single number `s = '2147483647'` — Correctly parsed into 32-bit integer.
  • Unary minus at beginning `s = '- (3 + (4 + 5))'` — Evaluates to -12.
  • Consecutive spaces `s = ' 2 - 1 + 2 '` — Spaces are skipped without affecting state.
  • Deeply nested parentheses `s = '((((5))))'` — Returns 5.

Interview Tips & Best Practices

How to communicate your thought process and address follow-ups in a live technical interview:

  1. Explain the state machine invariant: num accumulates digits, sign holds pending operation, result holds committed subtotal.
  2. Demonstrate how ( and ) create and resolve stacked sub-scopes.
  3. Remind the interviewer that division/multiplication are absent in Basic Calculator I, allowing the efficient sign-tracking pattern.

Quick Comprehension Check

Test your intuitive understanding of the core concept in 30 seconds:

1.Why do we push both result and sign onto the stack when ( is encountered?

2.What must be done at the very end after the loop finishes?