113. Basic Calculator
π LeetCode Problem: 224. Basic Calculator
π Difficulty: Hard
π·οΈ Topics: Stack, String, Math
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:
Input: s = "1 + 1"
Output: 2
Example 2:
Input: s = " 2-1 + 2 "
Output: 3
Example 3:
Input: s = "(1+(4+5+2)-3)+(6+8)"
Output: 23
Constraints:
- 1 <= s.length <= 3 * 10^5
- s consists of digits, '+', '-', '(', ')', and ' '.
- s represents a valid expression.
π Understanding the Problem First
Let's start with what we're trying to do.
The Simple Case - No Parentheses
"1 + 2 - 3"
Reading left to right:
Start with 0
See 1 β add it β 0 + 1 = 1
See + β ok, continue
See 2 β add it β 1 + 2 = 3
See - β ok, continue
See 3 β subtract it β 3 - 3 = 0
Result: 0
This is just addition and subtraction!
The Hard Case - With Parentheses
"5 - (3 + 2)"
What does this mean?
First: Calculate what's inside ()
(3 + 2) = 5
Then: Use that result
5 - 5 = 0
The parentheses create a "mini calculation" that we do first.
π‘ The Core Challenge
How do we handle parentheses?
"5 - (3 + 2)"
β βββββββ
β β
outer inner
Problem:
When we see '(', we need to:
1. PAUSE the outer calculation "5 -"
2. START a new inner calculation "3 + 2"
3. When we see ')', RESUME outer and use inner result
How to PAUSE and RESUME? π€
π― Building the Intuition
Insight 1: We Only Add and Subtract
Even subtraction can be thought of as adding a negative number!
"5 - 3" is the same as "5 + (-3)"
So our calculation is really just:
result = result + number1
result = result + number2
result = result + number3
...
Where numbers can be positive or negative!
Insight 2: Track "What to Do Next"
When we see an operator, we need to remember it for the NEXT number.
"5 - 3"
See '5': We have the number 5
See '-': Remember "the next number should be SUBTRACTED"
See '3': Apply what we remembered: 5 - 3 = 2
We need a variable to "remember" the operation!
Insight 3: Parentheses Create Nested Calculations
Think of parentheses like a Russian doll:
Outer calculation: 5 - ???
β
Inner calculation: 3 + 2 = 5
β
Back to outer: 5 - 5 = 0
We need to:
- Save the outer state before entering inner
- Calculate inner completely
- Come back and finish outer
This is EXACTLY what a stack does!
π¨ The Three Variables We Need
Let me explain each variable and WHY we need it.
Variable 1: result
int result = 0;
This tracks our RUNNING SUM.
Example: "1 + 2 - 3"
After '1': result = 1
After '2': result = 3
After '3': result = 0
Simple! This is just our running total.
Variable 2: number
int number = 0;
This builds multi-digit numbers.
Why needed?
"12 + 3"
Can't process '1' immediately (it's part of 12!)
'1': number = 1
'2': number = 12 (1 Γ 10 + 2)
'+': NOW use 12
We accumulate digits until we hit an operator.
Variable 3: sign
This is the CONFUSING one. Let me explain clearly.
int sign = 1;
This is NOT the sign of the result!
This tells us: "How should I use the next number?"
sign = 1 means: ADD the next number
sign = -1 means: SUBTRACT the next number
Example: "5 - 3"
See '5': number = 5
See '-':
First, use current number: result = 0 + 1Γ5 = 5
Then, remember for next: sign = -1
See '3': number = 3
End:
Use current number with remembered operation:
result = 5 + (-1)Γ3 = 5 - 3 = 2 β
Why use sign Γ number?
Instead of writing:
if (lastOperator == '+')
result = result + number;
else if (lastOperator == '-')
result = result - number;
We write ONE line:
result = result + sign Γ number;
When sign = 1: result + 1Γnumber = result + number β
When sign = -1: result + (-1)Γnumber = result - number β
π― Handling Parentheses - The Key Insight
What Happens at '('?
"5 - (3 + 2)"
β
Here!
Before '(':
result = 5
sign = -1 (because we just saw '-')
What these mean:
result = 5 β "We have calculated 5 so far"
sign = -1 β "We're going to SUBTRACT what's in ()"
When we see '(':
We need to SAVE these two values!
Why? Because we'll need them when we see ')'!
What Do We Save on the Stack?
Two numbers:
1. result (what we calculated so far)
2. sign (what we'll do with the inner result)
Example: "5 - (3 + 2)"
At '(':
stack.push(5) // Save result
stack.push(-1) // Save sign
Why save BOTH?
result tells us: "We had 5"
sign tells us: "We'll SUBTRACT the inner result"
Then we reset:
result = 0
sign = 1
And start calculating inside () from scratch!
What Happens at ')'?
We've been calculating inside ().
Now we're done and need to go back to outer calculation.
Example: "5 - (3 + 2)"
βββββββ
After calculating inside: result = 5 (from 3+2)
At ')':
1. Get back saved values:
savedSign = stack.pop() = -1
savedResult = stack.pop() = 5
2. Combine inner result with outer:
result = savedResult + savedSign Γ result
result = 5 + (-1) Γ 5
result = 5 - 5 = 0 β
The formula applies the inner result to the outer calculation!
π Complete Example Walkthrough
Let me trace "5 - (3 + 2)" in detail, focusing on intuition.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Initial State
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
stack = []
result = 0 // Running sum
number = 0 // Current number being built
sign = 1 // How to use next number (start with +)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Character: '5'
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
It's a digit!
Build the number: number = 5
State:
result = 0
number = 5
sign = 1
Meaning: "We're building the number 5"
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Character: ' ' (space)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Skip it.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Character: '-'
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
It's an operator!
Step 1: Process the number we built (5)
result = result + sign Γ number
result = 0 + 1 Γ 5 = 5
Now result = 5 β
Step 2: Reset number for next one
number = 0
Step 3: Remember operation for NEXT number
sign = -1 (next number will be subtracted)
State:
result = 5 // We have 5
number = 0
sign = -1 // Next number will be subtracted
stack = []
Meaning:
"We have 5 so far"
"The next number we see should be SUBTRACTED"
Think of it as: "5 - ???" (waiting for next number)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Character: ' ' (space)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Skip it.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Character: '('
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Opening parenthesis!
This is KEY! We're entering a nested calculation.
Current state before '(':
result = 5 // What we calculated
sign = -1 // What we'll do with inner result
Step 1: SAVE these to stack
stack.push(result) β push 5
stack.push(sign) β push -1
stack = [5, -1]
Why save both?
- We'll need result (5) to continue outer calculation
- We'll need sign (-1) to know we're SUBTRACTING inner result
Step 2: RESET for inner calculation
result = 0
sign = 1
State:
stack = [5, -1] // Saved outer state
result = 0 // Fresh start inside ()
number = 0
sign = 1 // Default to addition
Meaning:
"Paused outer: We had 5, we'll subtract what's inside"
"Starting inner: Clean slate, calculating from 0"
Visual:
Outer: 5 - ??? (PAUSED, saved on stack)
Inner: 0 (STARTING NOW)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Character: '3'
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
It's a digit!
Build the number: number = 3
State:
stack = [5, -1]
result = 0
number = 3
sign = 1
Meaning: "Inside (), building number 3"
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Character: ' ' (space)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Skip it.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Character: '+'
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
It's an operator!
Step 1: Process number (3)
result = result + sign Γ number
result = 0 + 1 Γ 3 = 3
Step 2: Reset number
number = 0
Step 3: Remember operation for next
sign = 1 (next will be added)
State:
stack = [5, -1]
result = 3 // Inside (), we have 3
number = 0
sign = 1 // Next will be added
Meaning: "Inside (), we have 3 so far, will add next number"
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Character: ' ' (space)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Skip it.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Character: '2'
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
It's a digit!
Build the number: number = 2
State:
stack = [5, -1]
result = 3
number = 2
sign = 1
Meaning: "Inside (), building number 2"
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Character: ')'
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Closing parenthesis! This is THE KEY MOMENT!
Step 1: Finish the inner calculation
result = result + sign Γ number
result = 3 + 1 Γ 2 = 5
Inner calculation complete: (3 + 2) = 5 β
Step 2: Reset number
number = 0
Step 3: Get back saved values from stack
savedSign = stack.pop() = -1
savedResult = stack.pop() = 5
stack = []
What we got back:
savedResult = 5 // This was our outer result
savedSign = -1 // This says SUBTRACT inner from outer
Step 4: Apply inner result to outer
result = savedResult + savedSign Γ result
result = 5 + (-1) Γ 5
result = 5 - 5
result = 0 β
Let me break down this formula:
savedResult = 5 // What we had before (
savedSign = -1 // Operation to apply (subtract)
result = 5 // Inner calculation result
Formula combines them:
5 + (-1) Γ 5
= 5 - 5
= 0
In plain English:
"We HAD 5 in outer calculation"
"We SUBTRACT the inner result (which is 5)"
"5 - 5 = 0"
State:
stack = [] // Back to outer level
result = 0 // Final answer!
number = 0
sign = -1 // (leftover, not used)
Meaning:
"Finished inner: (3 + 2) = 5"
"Applied to outer: 5 - 5 = 0"
"We're done!"
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
End of String
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Check if any number left:
result = result + sign Γ number
result = 0 + (-1) Γ 0 = 0
(No remaining number)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
FINAL ANSWER: 0 β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Verification: 5 - (3 + 2) = 5 - 5 = 0 β
π― Understanding the Key Formula
At ')': result = savedResult + savedSign Γ result
This is the MOST IMPORTANT line. Let me explain it clearly.
What each piece means:
savedResult: What we had BEFORE entering (
savedSign: How to apply inner result (+1 or -1)
result: What we calculated INSIDE ()
The formula: savedResult + savedSign Γ result
Example 1: "5 - (3 + 2)"
Before '(': savedResult=5, savedSign=-1
Inside '()': result = 5 (from 3+2)
Apply:
5 + (-1) Γ 5
= 5 - 5
= 0 β
Translation: "We had 5, subtract inner result 5, get 0"
Example 2: "5 + (3 + 2)"
Before '(': savedResult=5, savedSign=+1
Inside '()': result = 5 (from 3+2)
Apply:
5 + (+1) Γ 5
= 5 + 5
= 10 β
Translation: "We had 5, add inner result 5, get 10"
Example 3: "-(3 + 2)"
Before '(': savedResult=0, savedSign=-1
Inside '()': result = 5 (from 3+2)
Apply:
0 + (-1) Γ 5
= 0 - 5
= -5 β
Translation: "We had 0, subtract inner result 5, get -5"
The formula does exactly what you'd do manually!
π» The Complete Algorithm
Now that we understand the intuition, here's the code:
import java.util.Stack;
class Solution {
public int calculate(String s) {
// This is similar to decode string problem.
Stack<int[]> stack = new Stack<>();
int res = 0; // overall result
// Purpose is to decide what to do next number with earlier number.
int sign = 1; // by default. Or it can be -1.
int k = 0; // to handle double digit numbers
for(char ch : s.toCharArray()) {
if(Character.isDigit(ch)) {
k = (k * 10) + (ch - '0');
} else if (ch == '+') {
res = res + (sign * k); // accumulate whatever is there before moving to next number
sign = 1;
k = 0;
} else if (ch == '-') {
res = res + (sign * k); // accumulate whatever is there before moving to next number
sign = -1;
k = 0;
} else if (ch == '(') {
// push the existing state before entering the new context.
stack.push(new int[] {res, sign});
// once saved the existing context, reset the values to build for the next context.
sign = 1;
res = 0;
} else if (ch == ')') {
res = res + (sign * k); // finish whatever we have till now
k = 0;
// in case of 5 - (3 + 2),
// popped: [5, -1]
int[] popped = stack.pop();
int savedResult = popped[0];
int savedSign = popped[1];
res = savedResult + (savedSign * res);
} else if (ch == ' ') {
continue;
}
}
res = res + (sign * k); // if the input string does not end with ). For example, 1 + 2
return res;
}
public static void main(String[] args) {
Solution s = new Solution();
System.out.println(s.calculate("1 + 1") == 2);
System.out.println(s.calculate("2-1 + 2") == 3);
System.out.println(s.calculate("(1+(4+5+2)-3)+(6+8)") == 23);
}
}
Time: O(n) - Single pass
Space: O(d) where d = max nesting depth
π― Why This Works - Summary
The Three Variables:
- result = Running sum (what we've calculated)
- number = Current number being built (for multi-digit)
- sign = How to use next number (+1 for add, -1 for subtract)
The Stack Role:
- Saves outer state when entering '('
- Stack stores TWO values: [result, sign]
- When we see ')', we restore and combine
The Key Operation:
result += sign Γ number
This ONE line handles both addition and subtraction!
The Key Formula at ')':
result = savedResult + savedSign Γ result
Combines outer and inner calculations!
β οΈ Common Mistakes
Mistake 1: Not processing number before operator
// β WRONG
if (ch == '+') {
sign = 1; // Forgot to add number first!
}
// β CORRECT
if (ch == '+') {
result += sign * number; // Process number first
number = 0;
sign = 1;
}
Mistake 2: Forgetting final number
// β WRONG
return result; // Last number not added!
// β CORRECT
result += sign * number; // Add last number
return result;
Mistake 3: Wrong pop order
// β WRONG (reversed!)
int savedResult = stack.pop();
int savedSign = stack.pop();
// β CORRECT (reverse of push order)
int savedSign = stack.pop(); // Last in
int savedResult = stack.pop(); // First in
Mistake 4: Not resetting after '('
// β WRONG
stack.push(result);
stack.push(sign);
// Forgot to reset!
// β CORRECT
stack.push(result);
stack.push(sign);
result = 0; // Reset
sign = 1; // Reset
π― The Core Intuition - Final Check
Ask yourself these questions:
-
What does
resulttrack? β Running sum of our calculation -
What does
numberdo? β Builds multi-digit numbers (12 = 1Γ10 + 2) -
What does
signmean? β How to use the NEXT number (+1 add, -1 subtract) -
Why save to stack at '('? β To remember outer state while calculating inner
-
What does the formula at ')' do? β Combines outer result with inner result
-
Why
result + sign Γ number? β One formula for both addition and subtraction
If you can answer these, you understand it! π
π Quick Revision
Variables:
- result: running sum
- number: current number (multi-digit)
- sign: +1 or -1 (add or subtract next)
At operator (+/-):
- Process: result += sign Γ number
- Reset: number = 0
- Update: sign = new operation
At '(':
- Save: [result, sign] to stack
- Reset: result=0, sign=1
At ')':
- Finish: result += sign Γ number
- Restore: pop sign, pop result
- Combine: result = savedResult + savedSign Γ result
End:
- Add: result += sign Γ number
π You've Got It!
The key insights:
- Everything is addition (subtraction = add negative)
- sign remembers operation for next number
- Stack saves state at '(' for later
- Formula combines inner with outer at ')'
This is genuinely a hard problem, but once you understand:
- Why we use sign Γ number
- What we save on stack
- How the formula works
It becomes clear! π―
Happy practicing! πͺβ¨
Note: Don't memorize the code - understand WHY each part exists. The intuition is: pause outer calculation, do inner calculation, then combine them. The stack makes this possible!