title: Basic Calculator slug: basic-calculator difficulty: hard leetcode_id: 224 leetcode_url: https://leetcode.com/problems/basic-calculator/ categories: - strings - stack - math patterns: - slug: monotonic-stack is_optimal: true function_signature: "def calculate(s: str) -> int:" test_cases: visible: - input: { s: "1 + 1" } expected: 2 - input: { s: " 2-1 + 2 " } expected: 3 - input: { s: "(1+(4+5+2)-3)+(6+8)" } expected: 23 hidden: - input: { s: "1" } expected: 1 - input: { s: "-1" } expected: -1 - input: { s: "-(3+2)" } expected: -5 - input: { s: "1-(5)" } expected: -4 - input: { s: "2147483647" } expected: 2147483647 - input: { s: "1-(-2)" } expected: 3 description: | 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()`. constraints: | - `1 <= s.length <= 3 * 10^5` - `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) - There will be no two consecutive operators in the input - Every number and running calculation will fit in a signed 32-bit integer examples: - input: 's = "1 + 1"' output: "2" explanation: "Simple addition of two numbers." - input: 's = " 2-1 + 2 "' output: "3" explanation: "2 - 1 = 1, then 1 + 2 = 3. Spaces are ignored." - input: 's = "(1+(4+5+2)-3)+(6+8)"' output: "23" explanation: "Inner parentheses: (4+5+2) = 11, so (1+11-3) = 9. Then (6+8) = 14. Finally 9 + 14 = 23." explanation: intuition: | Imagine you're evaluating a mathematical expression by hand. When you encounter parentheses, you mentally "pause" what you were doing, compute the inner expression first, and then come back to where you left off. This is exactly what a **stack** enables programmatically. Think of the stack as a "memory" for the outer context. When you see an opening parenthesis `(`, you save your current progress (the running result and the sign before the parenthesis) onto the stack. Then you start fresh to compute the inner expression. When you hit a closing parenthesis `)`, you pop the saved context and combine it with the inner result. The key insight is that addition and subtraction are **left-associative** and have the **same precedence**, so we can evaluate the expression in a single left-to-right pass. The only complication is parentheses, which create nested scopes that we handle with a stack. For the sign, we track whether the next number should be added or subtracted. A `+` means add (sign = 1), and a `-` means subtract (sign = -1). Unary minus (like `-5` or `-(3+2)`) is handled naturally by setting the sign before parsing the number or entering the parentheses. approach: | We solve this using a **Stack-Based Single Pass** approach: **Step 1: Initialise variables** - `result`: Set to `0` to accumulate the running total - `sign`: Set to `1` (positive) representing whether to add or subtract the next value - `stack`: Empty list to save context when entering parentheses - `i`: Index to iterate through the string   **Step 2: Iterate through each character** - **Digit**: Build the full number (could be multi-digit), then add `sign * number` to result - **`+`**: Set sign to `1` (next value will be added) - **`-`**: Set sign to `-1` (next value will be subtracted) - **`(`**: Push current `result` and `sign` onto stack, then reset `result = 0` and `sign = 1` to start evaluating the sub-expression - **`)`**: Pop the saved sign and previous result from the stack. Compute `popped_result + popped_sign * result` to combine inner result with outer context - **Space**: Skip whitespace characters   **Step 3: Return the result** - After processing all characters, `result` contains the final evaluated value   The stack stores pairs of `(previous_result, sign_before_parenthesis)`. This lets us "freeze" the outer computation, evaluate the inner expression independently, and then seamlessly merge them when we close the parenthesis. common_pitfalls: - title: Not Handling Multi-Digit Numbers description: | A common mistake is treating each digit character as a separate number. For example, `"123"` should be parsed as the number `123`, not `1`, `2`, `3`. You need a loop that continues reading digits while the current character is a digit, building the number: `num = num * 10 + int(char)`. wrong_approach: "Treating each digit as a separate number" correct_approach: "Build multi-digit numbers in a loop" - title: Forgetting Unary Minus description: | The problem states that `-` can be used as a unary operator, like `-1` or `-(3+2)`. If you only handle binary subtraction, expressions like `"-(1+2)"` will fail. The sign variable naturally handles this: when we see `-` followed by `(`, we push the current state with sign `-1`, so the entire inner result gets negated. wrong_approach: "Only handling binary subtraction" correct_approach: "Track sign separately; it applies to both numbers and sub-expressions" - title: Incorrect Stack Order description: | When pushing to and popping from the stack, order matters. You need to push `result` first, then `sign`, and pop in reverse order (sign first, then result). If you get this backwards, you'll apply the wrong sign or add to the wrong accumulated value. wrong_approach: "Inconsistent push/pop order" correct_approach: "Push (result, sign) in order; pop sign first, then result" - title: Not Resetting After Opening Parenthesis description: | After pushing the current context onto the stack, you must reset `result = 0` and `sign = 1` to start evaluating the inner expression from scratch. Forgetting to reset means you'll incorrectly mix the outer and inner computations. wrong_approach: "Continuing with old result/sign after '('" correct_approach: "Reset result = 0 and sign = 1 after pushing to stack" key_takeaways: - "**Stack for nested structures**: Stacks are ideal for problems involving parentheses or any nested scope. Push context on open, pop on close." - "**Sign tracking**: Instead of handling `+` and `-` as operators between terms, track a sign multiplier that applies to the next value." - "**Single pass efficiency**: Despite the apparent complexity, expression evaluation with `+`, `-`, and parentheses can be done in O(n) time." - "**Foundation for harder calculators**: This pattern extends to Basic Calculator II (with `*` and `/`) and III (with all operators and parentheses)." time_complexity: "O(n). We iterate through each character in the string exactly once." space_complexity: "O(n). In the worst case with deeply nested parentheses like `(((...)))`, the stack stores O(n) pairs." solutions: - approach_name: Stack-Based Single Pass is_optimal: true code: | def calculate(s: str) -> int: result = 0 # Running total for current scope sign = 1 # 1 for positive, -1 for negative stack = [] # Stores (result, sign) when entering parentheses i = 0 n = len(s) while i < n: char = s[i] if char.isdigit(): # Build the full number (could be multi-digit) num = 0 while i < n and s[i].isdigit(): num = num * 10 + int(s[i]) i += 1 # Add (or subtract) this number to our result result += sign * num continue # i already advanced past the number elif char == '+': # Next number should be added sign = 1 elif char == '-': # Next number should be subtracted sign = -1 elif char == '(': # Save current context and start fresh stack.append(result) stack.append(sign) result = 0 sign = 1 elif char == ')': # Combine inner result with saved outer context prev_sign = stack.pop() prev_result = stack.pop() result = prev_result + prev_sign * result # Skip spaces (char == ' ') i += 1 return result explanation: | **Time Complexity:** O(n) — Single pass through the string. **Space Complexity:** O(n) — Stack depth proportional to nesting level, worst case O(n). We process each character once. Digits form multi-digit numbers, operators update the sign, and parentheses push/pop context from the stack. The sign variable elegantly handles both binary subtraction and unary minus. - approach_name: Recursive Descent is_optimal: false code: | def calculate(s: str) -> int: # Remove all spaces for easier parsing s = s.replace(' ', '') index = [0] # Use list for mutable reference in nested function def parse_expression() -> int: result = 0 sign = 1 while index[0] < len(s): char = s[index[0]] if char.isdigit(): # Parse multi-digit number num = 0 while index[0] < len(s) and s[index[0]].isdigit(): num = num * 10 + int(s[index[0]]) index[0] += 1 result += sign * num continue elif char == '+': sign = 1 index[0] += 1 elif char == '-': sign = -1 index[0] += 1 elif char == '(': index[0] += 1 # Skip '(' # Recursively evaluate sub-expression inner = parse_expression() result += sign * inner elif char == ')': index[0] += 1 # Skip ')' return result # Return to caller else: index[0] += 1 # Skip unexpected characters return result return parse_expression() explanation: | **Time Complexity:** O(n) — Each character is visited once. **Space Complexity:** O(n) — Recursion depth proportional to nesting level. This approach uses recursion to handle parentheses. When we see `(`, we recursively call `parse_expression()` to evaluate the inner content. When we see `)`, we return the inner result to the caller. The recursion stack implicitly does what the explicit stack does in the iterative solution. While equally efficient, this approach may hit Python's recursion limit for very deeply nested expressions, making the iterative stack solution preferred.