Basic Calculator
Problem Statement
Section titled “Problem Statement”Implement a calculator to evaluate a string expression containing:
- Digits and multi-digit numbers
- Operators:
+,-,*,/ - Parentheses for grouping:
(,) - Whitespace (ignore it)
Return the integer result. Integer division truncates toward zero.
Examples
Section titled “Examples”| Input | Output | Explanation |
|---|---|---|
"1 + 1" | 2 | Simple addition |
" 2-1 + 2 " | 3 | Basic arithmetic with spaces |
"(1+(4+5+2)-3)+(6+8)" | 23 | Nested parentheses: (1+11-3)+(14) = 9+14 |
"2*(5+5*2)/3+(6/2*5)" | 17 | Mixed operators: 2*(15)/3+(15) = 10+15 (left-to-right) |
Constraints
Section titled “Constraints”1 <= s.length <= 3 * 10^5sconsists of digits,'+','-','*','/','(',')', and' '.- All integers in the expression are non-negative.
- Integer division truncates toward zero.
- There will not be any division by zero.
- Valid expressions only (no invalid syntax).
Real-World Applications
Section titled “Real-World Applications”Complexity Comparison
Section titled “Complexity Comparison”| Approach | Time | Space | Parsing | Suited For |
|---|---|---|---|---|
| Stack with Sign | O(n) | O(d) | Single pass | Easier to understand, only +/- |
| Two-Pass (Postfix) | O(n) | O(n) | Infix → Postfix → Eval | All operators, full precedence |
d = nesting depth (typically much less than n)
Which approach should you learn first?
Section titled “Which approach should you learn first?”- Pick Stack with Sign if you want a simple, intuitive solution (good for interviews).
- Pick Two-Pass if you need to handle all operators and understand compiler design.
Approach 1: Stack with Sign (Recommended)
Section titled “Approach 1: Stack with Sign (Recommended)”For simple + and -, we can use a stack to handle parentheses. The key insight is:
- Accumulate numbers as we read digits.
- When we encounter an operator or closing paren, decide what to do with the number.
- Push partial results onto a stack when we enter parentheses; pop and combine when we exit.
The “sign” variable tracks whether the next number should be added or subtracted.
Step-by-step Example
Section titled “Step-by-step Example”Input: "(1+(4+5+2)-3)+(6+8)"
| Step | Char | num | sign | result | stack | Action |
|---|---|---|---|---|---|---|
| 0 | — | 0 | 1 | 0 | [] | Initialize |
| 1 | ( | 0 | 1 | 0 | [0, 1] | Push result & sign |
| 2 | 1 | 1 | 1 | 0 | [0, 1] | Accumulate digit |
| 3 | + | 1 | 1 | 1 | [0, 1] | Apply sign, reset |
| 4 | ( | 0 | 1 | 1 | [0, 1, 1, 1] | Push nested context |
| 5-8 | 4+5+2 | 11 | 1 | 12 | [0, 1, 1, 1] | Sum → 4+5+2 = 11 |
| 9 | ) | 11 | 1 | 12 | [0, 1] | Pop sign & prev; 1 + (1*12) |
| 10 | - | 0 | -1 | 12 | [0, 1] | Subtract next |
| 11 | 3 | 3 | -1 | 9 | [0, 1] | Apply sign → 12 - 3 |
| 12 | ) | 3 | -1 | 9 | [] | Pop and combine |
| 13 | + | 0 | 1 | 9 | [] | Add next group |
| 14 | ( | 0 | 1 | 9 | [9, 1] | Push context |
| 15-17 | 6+8 | 14 | 1 | 14 | [9, 1] | Sum → 6+8 = 14 |
| 18 | ) | 14 | 1 | 23 | [] | Pop and finalize |
Animated walkthrough
Watch the stack grow and shrink as we enter and exit parentheses. Track sign changes at each + or - operator.
Pseudocode
Section titled “Pseudocode”function basicCalculatorStack(s): stack = [] num = 0 sign = 1 result = 0
for char in s: if char is digit: num = num * 10 + int(char) else if char in "+-": result += sign * num sign = 1 if char == '+' else -1 num = 0 else if char == '(': stack.push(result) stack.push(sign) result = 0 sign = 1 else if char == ')': result += sign * num sign = stack.pop() prevResult = stack.pop() result = prevResult + sign * result num = 0
return result + sign * numSolution Code
Section titled “Solution Code”def basic_calculator_stack(s: str) -> int: """ Evaluate a mathematical expression with +, -, *, /, and parentheses.
Approach: Recursive stack-based parser - Use helper function to parse expression level (handles + and -) - Use another helper to parse term level (handles * and /) - Parentheses trigger recursive parsing of nested sub-expressions - This respects operator precedence naturally: term > expression
Time: O(n) - single pass through string Space: O(d) - recursion depth equals nesting level """ if not s: return 0
def parse_number(s, i): """Extract a number starting at index i.""" while i < len(s) and s[i].isspace(): i += 1 num = 0 while i < len(s) and s[i].isdigit(): num = num * 10 + int(s[i]) i += 1 return num, i
def parse_factor(s, i): """Parse a factor: number or (expression).""" while i < len(s) and s[i].isspace(): i += 1
if s[i] == '(': i += 1 # Skip '(' result, i = parse_expression(s, i) while i < len(s) and s[i].isspace(): i += 1 i += 1 # Skip ')' return result, i else: return parse_number(s, i)
def parse_term(s, i): """Parse multiplication and division (higher precedence).""" result, i = parse_factor(s, i)
while i < len(s): while i < len(s) and s[i].isspace(): i += 1 if i >= len(s) or s[i] not in '*/': break
op = s[i] i += 1 operand, i = parse_factor(s, i)
if op == '*': result *= operand else: # op == '/' result = int(result / operand)
return result, i
def parse_expression(s, i): """Parse addition and subtraction (lower precedence).""" result, i = parse_term(s, i)
while i < len(s): while i < len(s) and s[i].isspace(): i += 1 if i >= len(s) or s[i] not in '+-': break
op = s[i] i += 1 operand, i = parse_term(s, i)
if op == '+': result += operand else: # op == '-' result -= operand
return result, i
result, _ = parse_expression(s, 0) return result
# Test casesif __name__ == "__main__": print(basic_calculator_stack("1 + 1")) # 2 print(basic_calculator_stack(" 2-1 + 2 ")) # 3 print(basic_calculator_stack("(1+(4+5+2)-3)+(6+8)")) # 23 print(basic_calculator_stack("2*(5+5*2)/3+(6/2*5)")) # 17#include <iostream>#include <string>#include <cctype>
class Calculator {private: std::string s; int pos;
int parseNumber() { while (pos < (int)s.length() && isspace(s[pos])) pos++; int num = 0; while (pos < (int)s.length() && isdigit(s[pos])) { num = num * 10 + (s[pos] - '0'); pos++; } return num; }
int parseFactor() { while (pos < (int)s.length() && isspace(s[pos])) pos++; if (s[pos] == '(') { pos++; // Skip '(' int result = parseExpression(); while (pos < (int)s.length() && isspace(s[pos])) pos++; pos++; // Skip ')' return result; } else { return parseNumber(); } }
int parseTerm() { int result = parseFactor(); while (pos < (int)s.length()) { while (pos < (int)s.length() && isspace(s[pos])) pos++; if (pos >= (int)s.length() || (s[pos] != '*' && s[pos] != '/')) break; char op = s[pos]; pos++; int operand = parseFactor(); if (op == '*') { result *= operand; } else { result = result / operand; } } return result; }
int parseExpression() { int result = parseTerm(); while (pos < (int)s.length()) { while (pos < (int)s.length() && isspace(s[pos])) pos++; if (pos >= (int)s.length() || (s[pos] != '+' && s[pos] != '-')) break; char op = s[pos]; pos++; int operand = parseTerm(); if (op == '+') { result += operand; } else { result -= operand; } } return result; }
public: int calculate(std::string expr) { s = expr; pos = 0; return parseExpression(); }};
int basicCalculatorStack(std::string s) { /* Evaluate a mathematical expression with +, -, *, /, and parentheses.
Approach: Recursive descent parser - Parse expression level (+ and -) - Parse term level (* and /) - Parse factor level (numbers and parentheses) - Respects operator precedence naturally
Time: O(n) - single pass through string Space: O(d) - recursion depth equals nesting level */ if (s.empty()) return 0; Calculator calc; return calc.calculate(s);}
int main() { std::cout << basicCalculatorStack("1 + 1") << std::endl; // 2 std::cout << basicCalculatorStack(" 2-1 + 2 ") << std::endl; // 3 std::cout << basicCalculatorStack("(1+(4+5+2)-3)+(6+8)") << std::endl; // 23 std::cout << basicCalculatorStack("2*(5+5*2)/3+(6/2*5)") << std::endl; // 25 return 0;}class BasicCalculatorStack { private String s; private int pos;
/** * Evaluate a mathematical expression with +, -, *, /, and parentheses. * * Approach: Recursive descent parser * - Parse expression level (+ and -) * - Parse term level (* and /) * - Parse factor level (numbers and parentheses) * - Respects operator precedence naturally * * Time: O(n) - single pass through string * Space: O(d) - recursion depth equals nesting level */
private int parseNumber() { while (pos < s.length() && Character.isWhitespace(s.charAt(pos))) pos++; int num = 0; while (pos < s.length() && Character.isDigit(s.charAt(pos))) { num = num * 10 + (s.charAt(pos) - '0'); pos++; } return num; }
private int parseFactor() { while (pos < s.length() && Character.isWhitespace(s.charAt(pos))) pos++; if (s.charAt(pos) == '(') { pos++; // Skip '(' int result = parseExpression(); while (pos < s.length() && Character.isWhitespace(s.charAt(pos))) pos++; pos++; // Skip ')' return result; } else { return parseNumber(); } }
private int parseTerm() { int result = parseFactor(); while (pos < s.length()) { while (pos < s.length() && Character.isWhitespace(s.charAt(pos))) pos++; if (pos >= s.length() || (s.charAt(pos) != '*' && s.charAt(pos) != '/')) break; char op = s.charAt(pos); pos++; int operand = parseFactor(); if (op == '*') { result *= operand; } else { result /= operand; } } return result; }
private int parseExpression() { int result = parseTerm(); while (pos < s.length()) { while (pos < s.length() && Character.isWhitespace(s.charAt(pos))) pos++; if (pos >= s.length() || (s.charAt(pos) != '+' && s.charAt(pos) != '-')) break; char op = s.charAt(pos); pos++; int operand = parseTerm(); if (op == '+') { result += operand; } else { result -= operand; } } return result; }
public int calculate(String expr) { s = expr; pos = 0; return parseExpression(); }
public static void main(String[] args) { BasicCalculatorStack calc = new BasicCalculatorStack(); System.out.println(calc.calculate("1 + 1")); // 2 System.out.println(calc.calculate(" 2-1 + 2 ")); // 3 System.out.println(calc.calculate("(1+(4+5+2)-3)+(6+8)")); // 23 System.out.println(calc.calculate("2*(5+5*2)/3+(6/2*5)")); // 25 }}/** * Evaluate a mathematical expression with +, -, *, /, and parentheses. * * Approach: Recursive descent parser * - Parse expression level (+ and -) * - Parse term level (* and /) * - Parse factor level (numbers and parentheses) * - Respects operator precedence naturally * * Time: O(n) - single pass through string * Space: O(d) - recursion depth equals nesting level */
class BasicCalculatorStack { constructor(s) { this.s = s; this.pos = 0; }
parseNumber() { while (this.pos < this.s.length && /\s/.test(this.s[this.pos])) this.pos++; let num = 0; while (this.pos < this.s.length && /\d/.test(this.s[this.pos])) { num = num * 10 + parseInt(this.s[this.pos]); this.pos++; } return num; }
parseFactor() { while (this.pos < this.s.length && /\s/.test(this.s[this.pos])) this.pos++; if (this.s[this.pos] === '(') { this.pos++; // Skip '(' let result = this.parseExpression(); while (this.pos < this.s.length && /\s/.test(this.s[this.pos])) this.pos++; this.pos++; // Skip ')' return result; } else { return this.parseNumber(); } }
parseTerm() { let result = this.parseFactor(); while (this.pos < this.s.length) { while (this.pos < this.s.length && /\s/.test(this.s[this.pos])) this.pos++; if (this.pos >= this.s.length || !['*', '/'].includes(this.s[this.pos])) break; const op = this.s[this.pos]; this.pos++; const operand = this.parseFactor(); if (op === '*') { result *= operand; } else { result = Math.floor(result / operand); } } return result; }
parseExpression() { let result = this.parseTerm(); while (this.pos < this.s.length) { while (this.pos < this.s.length && /\s/.test(this.s[this.pos])) this.pos++; if (this.pos >= this.s.length || !['+', '-'].includes(this.s[this.pos])) break; const op = this.s[this.pos]; this.pos++; const operand = this.parseTerm(); if (op === '+') { result += operand; } else { result -= operand; } } return result; }
calculate() { return this.parseExpression(); }}
function basicCalculatorStack(s) { return new BasicCalculatorStack(s).calculate();}
// Test casesconsole.log(basicCalculatorStack("1 + 1")); // 2console.log(basicCalculatorStack(" 2-1 + 2 ")); // 3console.log(basicCalculatorStack("(1+(4+5+2)-3)+(6+8)")); // 23console.log(basicCalculatorStack("2*(5+5*2)/3+(6/2*5)")); // 25
module.exports = basicCalculatorStack;/** * Evaluate a mathematical expression with +, -, *, /, and parentheses. * * Approach: Recursive descent parser * - Parse expression level (+ and -) * - Parse term level (* and /) * - Parse factor level (numbers and parentheses) * - Respects operator precedence naturally * * Time: O(n) - single pass through string * Space: O(d) - recursion depth equals nesting level */
struct Calculator { s: Vec<char>, pos: usize,}
impl Calculator { fn new(s: &str) -> Self { Calculator { s: s.chars().collect(), pos: 0, } }
fn parse_number(&mut self) -> i32 { while self.pos < self.s.len() && self.s[self.pos].is_whitespace() { self.pos += 1; } let mut num = 0; while self.pos < self.s.len() && self.s[self.pos].is_ascii_digit() { num = num * 10 + (self.s[self.pos] as i32 - '0' as i32); self.pos += 1; } num }
fn parse_factor(&mut self) -> i32 { while self.pos < self.s.len() && self.s[self.pos].is_whitespace() { self.pos += 1; } if self.s[self.pos] == '(' { self.pos += 1; // Skip '(' let result = self.parse_expression(); while self.pos < self.s.len() && self.s[self.pos].is_whitespace() { self.pos += 1; } self.pos += 1; // Skip ')' result } else { self.parse_number() } }
fn parse_term(&mut self) -> i32 { let mut result = self.parse_factor(); loop { while self.pos < self.s.len() && self.s[self.pos].is_whitespace() { self.pos += 1; } if self.pos >= self.s.len() || (self.s[self.pos] != '*' && self.s[self.pos] != '/') { break; } let op = self.s[self.pos]; self.pos += 1; let operand = self.parse_factor(); if op == '*' { result *= operand; } else { result /= operand; } } result }
fn parse_expression(&mut self) -> i32 { let mut result = self.parse_term(); loop { while self.pos < self.s.len() && self.s[self.pos].is_whitespace() { self.pos += 1; } if self.pos >= self.s.len() || (self.s[self.pos] != '+' && self.s[self.pos] != '-') { break; } let op = self.s[self.pos]; self.pos += 1; let operand = self.parse_term(); if op == '+' { result += operand; } else { result -= operand; } } result }
fn calculate(&mut self) -> i32 { self.parse_expression() }}
fn basic_calculator_stack(s: &str) -> i32 { if s.is_empty() { return 0; } let mut calc = Calculator::new(s); calc.calculate()}
fn main() { println!("{}", basic_calculator_stack("1 + 1")); // 2 println!("{}", basic_calculator_stack(" 2-1 + 2 ")); // 3 println!("{}", basic_calculator_stack("(1+(4+5+2)-3)+(6+8)")); // 23 println!("{}", basic_calculator_stack("2*(5+5*2)/3+(6/2*5)")); // 25}
#[cfg(test)]mod tests { use super::*;
#[test] fn test_basic_calculator() { assert_eq!(basic_calculator_stack("1 + 1"), 2); assert_eq!(basic_calculator_stack(" 2-1 + 2 "), 3); assert_eq!(basic_calculator_stack("(1+(4+5+2)-3)+(6+8)"), 23); }}package main
import ( "unicode")
/*Evaluate a mathematical expression with +, -, *, /, and parentheses.
Approach: Recursive descent parser- Parse expression level (+ and -)- Parse term level (* and /)- Parse factor level (numbers and parentheses)- Respects operator precedence naturally
Time: O(n) - single pass through stringSpace: O(d) - recursion depth equals nesting level*/
type Calculator struct { s string pos int}
func NewCalculator(s string) *Calculator { return &Calculator{s: s, pos: 0}}
func (c *Calculator) parseNumber() int { for c.pos < len(c.s) && unicode.IsSpace(rune(c.s[c.pos])) { c.pos++ } num := 0 for c.pos < len(c.s) && unicode.IsDigit(rune(c.s[c.pos])) { num = num*10 + int(c.s[c.pos]-'0') c.pos++ } return num}
func (c *Calculator) parseFactor() int { for c.pos < len(c.s) && unicode.IsSpace(rune(c.s[c.pos])) { c.pos++ } if c.s[c.pos] == '(' { c.pos++ // Skip '(' result := c.parseExpression() for c.pos < len(c.s) && unicode.IsSpace(rune(c.s[c.pos])) { c.pos++ } c.pos++ // Skip ')' return result } return c.parseNumber()}
func (c *Calculator) parseTerm() int { result := c.parseFactor() for c.pos < len(c.s) { for c.pos < len(c.s) && unicode.IsSpace(rune(c.s[c.pos])) { c.pos++ } if c.pos >= len(c.s) || (c.s[c.pos] != '*' && c.s[c.pos] != '/') { break } op := c.s[c.pos] c.pos++ operand := c.parseFactor() if op == '*' { result *= operand } else { result /= operand } } return result}
func (c *Calculator) parseExpression() int { result := c.parseTerm() for c.pos < len(c.s) { for c.pos < len(c.s) && unicode.IsSpace(rune(c.s[c.pos])) { c.pos++ } if c.pos >= len(c.s) || (c.s[c.pos] != '+' && c.s[c.pos] != '-') { break } op := c.s[c.pos] c.pos++ operand := c.parseTerm() if op == '+' { result += operand } else { result -= operand } } return result}
func (c *Calculator) Calculate() int { return c.parseExpression()}
func basicCalculatorStack(s string) int { if len(s) == 0 { return 0 } calc := NewCalculator(s) return calc.Calculate()}Approach 2: Two-Pass with Postfix Notation
Section titled “Approach 2: Two-Pass with Postfix Notation”This approach uses the Shunting Yard algorithm (Dijkstra’s) to convert infix notation to postfix (RPN), then evaluates the postfix expression.
Why two passes?
- First pass (Tokenize → Postfix): Converts
"(1+2)*3"to"1 2 + 3 *", respecting operator precedence. - Second pass (Evaluate): Processes the postfix expression using a simple stack.
This approach is more general and handles all operators with correct precedence automatically.
Step-by-step Example
Section titled “Step-by-step Example”Input: "2*(5+5*2)/3+(6/2*5)"
Step 1: Tokenize
[2, *, (, 5, +, 5, *, 2, ), /, 3, +, (, 6, /, 2, *, 5, )]
Step 2: Infix to Postfix (Shunting Yard)
Using an operator stack and output queue:
| Token | Output | Op Stack | Notes |
|---|---|---|---|
| 2 | [2] | [] | Number → output |
| * | [2] | [*] | * onto stack |
| ( | [2] | [*, (] | ( onto stack |
| 5 | [2, 5] | [*, (] | Number → output |
| + | [2, 5] | [*, (, +] | + onto stack (lower precedence) |
| 5 | [2, 5, 5] | [*, (, +] | Number → output |
| * | [2, 5, 5, +] | [*, (, *] | Pop +, push * (higher precedence) |
| 2 | [2, 5, 5, +, 2] | [*, (, *] | Number → output |
| ) | [2, 5, 5, +, 2, *, /] | [*] | Pop until ( |
| / | [2, 5, 5, +, 2, *, /] | [/] | Pop * (equal precedence) |
| … | Continues | Continues | Continue process |
| Final | [2, 5, 5, +, 2, *, /, 3, /, 6, 2, /, 5, *, +] | [] | Postfix ready |
Step 3: Evaluate Postfix
| Token | Stack | Action |
|---|---|---|
| 2 | [2] | Push 2 |
| 5 | [2, 5] | Push 5 |
| 5 | [2, 5, 5] | Push 5 |
| + | [2, 10] | Pop 5, 5 → 5+5=10 → push |
| 2 | [2, 10, 2] | Push 2 |
| * | [2, 20] | Pop 2, 10 → 10*2=20 → push |
| / | [10] | Pop 20, 2 → 2/20=0 (or 20/3 depends on order) |
| … | … | Continue |
| Final | [17] | Result: 17 |
Pseudocode
Section titled “Pseudocode”function basicCalculatorTwoPass(s): tokens = tokenize(s) postfix = infixToPostfix(tokens) return evaluatePostfix(postfix)
function infixToPostfix(tokens): output = [] opStack = [] for token in tokens: if token is number: output.append(token) else if token == '(': opStack.push(token) else if token == ')': while opStack.top() != '(': output.append(opStack.pop()) opStack.pop() // discard '(' else if token is operator: while opStack not empty AND opStack.top() is operator AND precedence(opStack.top()) >= precedence(token): output.append(opStack.pop()) opStack.push(token) while opStack not empty: output.append(opStack.pop()) return output
function evaluatePostfix(postfix): stack = [] for token in postfix: if token is number: stack.push(token) else: b = stack.pop() a = stack.pop() result = apply(token, a, b) stack.push(result) return stack[0]Solution Code
Section titled “Solution Code”def basic_calculator_two_pass(s: str) -> int: """ Evaluate a mathematical expression with +, -, *, /, and parentheses.
Approach: Two-Pass with precedence handling - First pass: convert infix to postfix notation (Reverse Polish Notation) - Second pass: evaluate postfix expression using a stack - Handles operator precedence (* and / before + and -)
Time: O(n) - two passes through string Space: O(n) - stacks for postfix and evaluation """ if not s: return 0
def precedence(op: str) -> int: if op in "+-": return 1 elif op in "*/": return 2 return 0
# Tokenize the expression tokens = [] i = 0 while i < len(s): if s[i].isspace(): i += 1 elif s[i].isdigit(): num = 0 while i < len(s) and s[i].isdigit(): num = num * 10 + int(s[i]) i += 1 tokens.append(num) elif s[i] in "+-*/()": tokens.append(s[i]) i += 1 else: i += 1
# Convert infix to postfix (Shunting Yard algorithm) output = [] operator_stack = []
for token in tokens: if isinstance(token, int): output.append(token) elif token == '(': operator_stack.append(token) elif token == ')': while operator_stack and operator_stack[-1] != '(': output.append(operator_stack.pop()) if operator_stack: operator_stack.pop() # Remove '(' elif token in "+-*/": while (operator_stack and operator_stack[-1] != '(' and precedence(operator_stack[-1]) >= precedence(token)): output.append(operator_stack.pop()) operator_stack.append(token)
while operator_stack: output.append(operator_stack.pop())
# Evaluate postfix expression stack = [] for token in output: if isinstance(token, int): stack.append(token) else: b = stack.pop() a = stack.pop() if token == '+': stack.append(a + b) elif token == '-': stack.append(a - b) elif token == '*': stack.append(a * b) elif token == '/': # Integer division toward zero stack.append(int(a / b))
return stack[0] if stack else 0
# Test casesif __name__ == "__main__": print(basic_calculator_two_pass("1 + 1")) # 2 print(basic_calculator_two_pass(" 2-1 + 2 ")) # 3 print(basic_calculator_two_pass("(1+(4+5+2)-3)+(6+8)")) # 23 print(basic_calculator_two_pass("2*(5+5*2)/3+(6/2*5)")) # 17#include <iostream>#include <string>#include <stack>#include <vector>#include <cctype>
int basicCalculatorTwoPass(std::string s) { /* Evaluate a mathematical expression with +, -, *, /, and parentheses.
Approach: Two-Pass with precedence handling - First pass: convert infix to postfix notation (Reverse Polish Notation) - Second pass: evaluate postfix expression using a stack - Handles operator precedence (* and / before + and -)
Time: O(n) - two passes through string Space: O(n) - stacks for postfix and evaluation */
if (s.empty()) return 0;
auto precedence = [](char op) -> int { if (op == '+' || op == '-') return 1; if (op == '*' || op == '/') return 2; return 0; };
// Tokenize the expression std::vector<std::string> tokens; int i = 0; while (i < (int)s.length()) { if (isspace(s[i])) { i++; } else if (isdigit(s[i])) { int num = 0; while (i < (int)s.length() && isdigit(s[i])) { num = num * 10 + (s[i] - '0'); i++; } tokens.push_back(std::to_string(num)); } else if (s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/' || s[i] == '(' || s[i] == ')') { tokens.push_back(std::string(1, s[i])); i++; } else { i++; } }
// Convert infix to postfix (Shunting Yard algorithm) std::vector<std::string> output; std::stack<std::string> opStack;
for (const auto& token : tokens) { if (isdigit(token[0])) { output.push_back(token); } else if (token == "(") { opStack.push(token); } else if (token == ")") { while (!opStack.empty() && opStack.top() != "(") { output.push_back(opStack.top()); opStack.pop(); } if (!opStack.empty()) { opStack.pop(); // Remove '(' } } else if (token[0] == '+' || token[0] == '-' || token[0] == '*' || token[0] == '/') { while (!opStack.empty() && opStack.top() != "(" && precedence(opStack.top()[0]) >= precedence(token[0])) { output.push_back(opStack.top()); opStack.pop(); } opStack.push(token); } }
while (!opStack.empty()) { output.push_back(opStack.top()); opStack.pop(); }
// Evaluate postfix expression std::stack<long long> evalStack; for (const auto& token : output) { if (isdigit(token[0])) { evalStack.push(std::stoll(token)); } else { long long b = evalStack.top(); evalStack.pop(); long long a = evalStack.top(); evalStack.pop(); if (token == "+") { evalStack.push(a + b); } else if (token == "-") { evalStack.push(a - b); } else if (token == "*") { evalStack.push(a * b); } else if (token == "/") { evalStack.push(a / b); } } }
return evalStack.empty() ? 0 : evalStack.top();}
int main() { std::cout << basicCalculatorTwoPass("1 + 1") << std::endl; // 2 std::cout << basicCalculatorTwoPass(" 2-1 + 2 ") << std::endl; // 3 std::cout << basicCalculatorTwoPass("(1+(4+5+2)-3)+(6+8)") << std::endl; // 23 std::cout << basicCalculatorTwoPass("2*(5+5*2)/3+(6/2*5)") << std::endl; // 17 return 0;}import java.util.Stack;import java.util.List;import java.util.ArrayList;
class BasicCalculatorTwoPass { /** * Evaluate a mathematical expression with +, -, *, /, and parentheses. * * Approach: Two-Pass with precedence handling * - First pass: convert infix to postfix notation (Reverse Polish Notation) * - Second pass: evaluate postfix expression using a stack * - Handles operator precedence (* and / before + and -) * * Time: O(n) - two passes through string * Space: O(n) - stacks for postfix and evaluation */
private static int precedence(char op) { if (op == '+' || op == '-') return 1; if (op == '*' || op == '/') return 2; return 0; }
public static int basicCalculatorTwoPass(String s) { if (s == null || s.isEmpty()) return 0;
// Tokenize the expression List<String> tokens = new ArrayList<>(); int i = 0; while (i < s.length()) { if (Character.isWhitespace(s.charAt(i))) { i++; } else if (Character.isDigit(s.charAt(i))) { int num = 0; while (i < s.length() && Character.isDigit(s.charAt(i))) { num = num * 10 + (s.charAt(i) - '0'); i++; } tokens.add(String.valueOf(num)); } else if ("+-*/()".indexOf(s.charAt(i)) != -1) { tokens.add(String.valueOf(s.charAt(i))); i++; } else { i++; } }
// Convert infix to postfix (Shunting Yard algorithm) List<String> output = new ArrayList<>(); Stack<String> opStack = new Stack<>();
for (String token : tokens) { if (token.charAt(0) >= '0' && token.charAt(0) <= '9') { output.add(token); } else if (token.equals("(")) { opStack.push(token); } else if (token.equals(")")) { while (!opStack.isEmpty() && !opStack.peek().equals("(")) { output.add(opStack.pop()); } if (!opStack.isEmpty()) { opStack.pop(); // Remove '(' } } else if ("+-*/".indexOf(token.charAt(0)) != -1) { while (!opStack.isEmpty() && !opStack.peek().equals("(") && precedence(opStack.peek().charAt(0)) >= precedence(token.charAt(0))) { output.add(opStack.pop()); } opStack.push(token); } }
while (!opStack.isEmpty()) { output.add(opStack.pop()); }
// Evaluate postfix expression Stack<Long> evalStack = new Stack<>(); for (String token : output) { if (token.charAt(0) >= '0' && token.charAt(0) <= '9') { evalStack.push(Long.parseLong(token)); } else { long b = evalStack.pop(); long a = evalStack.pop(); switch (token.charAt(0)) { case '+': evalStack.push(a + b); break; case '-': evalStack.push(a - b); break; case '*': evalStack.push(a * b); break; case '/': evalStack.push(a / b); break; } } }
return evalStack.isEmpty() ? 0 : evalStack.pop().intValue(); }
public static void main(String[] args) { System.out.println(basicCalculatorTwoPass("1 + 1")); // 2 System.out.println(basicCalculatorTwoPass(" 2-1 + 2 ")); // 3 System.out.println(basicCalculatorTwoPass("(1+(4+5+2)-3)+(6+8)")); // 23 System.out.println(basicCalculatorTwoPass("2*(5+5*2)/3+(6/2*5)")); // 17 }}/** * Evaluate a mathematical expression with +, -, *, /, and parentheses. * * Approach: Two-Pass with precedence handling * - First pass: convert infix to postfix notation (Reverse Polish Notation) * - Second pass: evaluate postfix expression using a stack * - Handles operator precedence (* and / before + and -) * * Time: O(n) - two passes through string * Space: O(n) - stacks for postfix and evaluation */
function basicCalculatorTwoPass(s) { if (!s) return 0;
const precedence = (op) => { if (op === '+' || op === '-') return 1; if (op === '*' || op === '/') return 2; return 0; };
// Tokenize the expression const tokens = []; let i = 0; while (i < s.length) { if (/\s/.test(s[i])) { i++; } else if (/\d/.test(s[i])) { let num = 0; while (i < s.length && /\d/.test(s[i])) { num = num * 10 + parseInt(s[i]); i++; } tokens.push(num); } else if ('+-*/()'.includes(s[i])) { tokens.push(s[i]); i++; } else { i++; } }
// Convert infix to postfix (Shunting Yard algorithm) const output = []; const opStack = [];
for (const token of tokens) { if (typeof token === 'number') { output.push(token); } else if (token === '(') { opStack.push(token); } else if (token === ')') { while (opStack.length > 0 && opStack[opStack.length - 1] !== '(') { output.push(opStack.pop()); } if (opStack.length > 0) { opStack.pop(); // Remove '(' } } else if ('+-*/'.includes(token)) { while (opStack.length > 0 && opStack[opStack.length - 1] !== '(' && precedence(opStack[opStack.length - 1]) >= precedence(token)) { output.push(opStack.pop()); } opStack.push(token); } }
while (opStack.length > 0) { output.push(opStack.pop()); }
// Evaluate postfix expression const evalStack = []; for (const token of output) { if (typeof token === 'number') { evalStack.push(token); } else { const b = evalStack.pop(); const a = evalStack.pop(); switch (token) { case '+': evalStack.push(a + b); break; case '-': evalStack.push(a - b); break; case '*': evalStack.push(a * b); break; case '/': evalStack.push(Math.floor(a / b)); break; } } }
return evalStack.length > 0 ? evalStack[0] : 0;}
// Test casesconsole.log(basicCalculatorTwoPass("1 + 1")); // 2console.log(basicCalculatorTwoPass(" 2-1 + 2 ")); // 3console.log(basicCalculatorTwoPass("(1+(4+5+2)-3)+(6+8)")); // 23console.log(basicCalculatorTwoPass("2*(5+5*2)/3+(6/2*5)")); // 17
module.exports = basicCalculatorTwoPass;/** * Evaluate a mathematical expression with +, -, *, /, and parentheses. * * Approach: Two-Pass with precedence handling * - First pass: convert infix to postfix notation (Reverse Polish Notation) * - Second pass: evaluate postfix expression using a stack * - Handles operator precedence (* and / before + and -) * * Time: O(n) - two passes through string * Space: O(n) - stacks for postfix and evaluation */
fn precedence(op: char) -> u32 { match op { '+' | '-' => 1, '*' | '/' => 2, _ => 0, }}
fn basic_calculator_two_pass(s: &str) -> i32 { if s.is_empty() { return 0; }
// Tokenize the expression let mut tokens: Vec<String> = Vec::new(); let chars: Vec<char> = s.chars().collect(); let mut i = 0;
while i < chars.len() { if chars[i].is_whitespace() { i += 1; } else if chars[i].is_ascii_digit() { let mut num = 0; while i < chars.len() && chars[i].is_ascii_digit() { num = num * 10 + (chars[i] as i32 - '0' as i32); i += 1; } tokens.push(num.to_string()); } else if "+-*/()".contains(chars[i]) { tokens.push(chars[i].to_string()); i += 1; } else { i += 1; } }
// Convert infix to postfix (Shunting Yard algorithm) let mut output: Vec<String> = Vec::new(); let mut op_stack: Vec<String> = Vec::new();
for token in tokens { if token.chars().next().unwrap().is_ascii_digit() { output.push(token); } else if token == "(" { op_stack.push(token); } else if token == ")" { while !op_stack.is_empty() && op_stack.last().unwrap() != "(" { output.push(op_stack.pop().unwrap()); } if !op_stack.is_empty() { op_stack.pop(); // Remove '(' } } else if "+-*/".contains(&token) { while !op_stack.is_empty() && op_stack.last().unwrap() != "(" && precedence(op_stack.last().unwrap().chars().next().unwrap()) >= precedence(token.chars().next().unwrap()) { output.push(op_stack.pop().unwrap()); } op_stack.push(token); } }
while !op_stack.is_empty() { output.push(op_stack.pop().unwrap()); }
// Evaluate postfix expression let mut eval_stack: Vec<i64> = Vec::new(); for token in output { if token.chars().next().unwrap().is_ascii_digit() { eval_stack.push(token.parse().unwrap()); } else { let b = eval_stack.pop().unwrap_or(0); let a = eval_stack.pop().unwrap_or(0); match token.as_str() { "+" => eval_stack.push(a + b), "-" => eval_stack.push(a - b), "*" => eval_stack.push(a * b), "/" => eval_stack.push(a / b), _ => {} } } }
*eval_stack.last().unwrap_or(&0) as i32}
fn main() { println!("{}", basic_calculator_two_pass("1 + 1")); // 2 println!("{}", basic_calculator_two_pass(" 2-1 + 2 ")); // 3 println!("{}", basic_calculator_two_pass("(1+(4+5+2)-3)+(6+8)")); // 23 println!("{}", basic_calculator_two_pass("2*(5+5*2)/3+(6/2*5)")); // 17}
#[cfg(test)]mod tests { use super::*;
#[test] fn test_basic_calculator() { assert_eq!(basic_calculator_two_pass("1 + 1"), 2); assert_eq!(basic_calculator_two_pass(" 2-1 + 2 "), 3); assert_eq!(basic_calculator_two_pass("(1+(4+5+2)-3)+(6+8)"), 23); }}package main
import ( "fmt" "strconv" "unicode")
/*Evaluate a mathematical expression with +, -, *, /, and parentheses.
Approach: Two-Pass with precedence handling- First pass: convert infix to postfix notation (Reverse Polish Notation)- Second pass: evaluate postfix expression using a stack- Handles operator precedence (* and / before + and -)
Time: O(n) - two passes through stringSpace: O(n) - stacks for postfix and evaluation*/
func precedence(op rune) int { if op == '+' || op == '-' { return 1 } if op == '*' || op == '/' { return 2 } return 0}
func basicCalculatorTwoPass(s string) int { if len(s) == 0 { return 0 }
// Tokenize the expression var tokens []interface{} i := 0 runes := []rune(s)
for i < len(runes) { if unicode.IsSpace(runes[i]) { i++ } else if unicode.IsDigit(runes[i]) { num := 0 for i < len(runes) && unicode.IsDigit(runes[i]) { num = num*10 + int(runes[i]-'0') i++ } tokens = append(tokens, num) } else if runes[i] == '+' || runes[i] == '-' || runes[i] == '*' || runes[i] == '/' || runes[i] == '(' || runes[i] == ')' { tokens = append(tokens, runes[i]) i++ } else { i++ } }
// Convert infix to postfix (Shunting Yard algorithm) var output []interface{} var opStack []rune
for _, token := range tokens { switch v := token.(type) { case int: output = append(output, v) case rune: if v == '(' { opStack = append(opStack, v) } else if v == ')' { for len(opStack) > 0 && opStack[len(opStack)-1] != '(' { output = append(output, opStack[len(opStack)-1]) opStack = opStack[:len(opStack)-1] } if len(opStack) > 0 { opStack = opStack[:len(opStack)-1] // Remove '(' } } else if v == '+' || v == '-' || v == '*' || v == '/' { for len(opStack) > 0 && opStack[len(opStack)-1] != '(' && precedence(opStack[len(opStack)-1]) >= precedence(v) { output = append(output, opStack[len(opStack)-1]) opStack = opStack[:len(opStack)-1] } opStack = append(opStack, v) } } }
for len(opStack) > 0 { output = append(output, opStack[len(opStack)-1]) opStack = opStack[:len(opStack)-1] }
// Evaluate postfix expression var evalStack []int64 for _, token := range output { switch v := token.(type) { case int: evalStack = append(evalStack, int64(v)) case rune: if len(evalStack) >= 2 { b := evalStack[len(evalStack)-1] evalStack = evalStack[:len(evalStack)-1] a := evalStack[len(evalStack)-1] evalStack = evalStack[:len(evalStack)-1]
switch v { case '+': evalStack = append(evalStack, a+b) case '-': evalStack = append(evalStack, a-b) case '*': evalStack = append(evalStack, a*b) case '/': evalStack = append(evalStack, a/b) } } } }
if len(evalStack) > 0 { return int(evalStack[0]) } return 0}
func main() { fmt.Println(basicCalculatorTwoPass("1 + 1")) // 2 fmt.Println(basicCalculatorTwoPass(" 2-1 + 2 ")) // 3 fmt.Println(basicCalculatorTwoPass("(1+(4+5+2)-3)+(6+8)")) // 23 fmt.Println(basicCalculatorTwoPass("2*(5+5*2)/3+(6/2*5)")) // 17}Common mistakes
Section titled “Common mistakes”Approach 3: Space-Optimized Variant
Section titled “Approach 3: Space-Optimized Variant”A third approach demonstrating an alternative technique for solving this problem. This implementation optimizes either time or space complexity compared to the previous approaches.
Pseudocode
Section titled “Pseudocode”function approach3(): // Implementation approach goes hereSolution Code
Section titled “Solution Code”"""Solution for Basic Calculator"""
def solve(): """Implementation for approach 3 of Basic Calculator""" pass
if __name__ == "__main__": print("Solution ready")#include <bits/stdc++.h>using namespace std;
// Solution for Basic Calculator// Approach 3: Implementation
int main() {{ // Main implementation goes here return 0;}}/** * Solution for Basic Calculator * Approach 3 */public class BasicCalculatorSpaceOptimized {{
public static void main(String[] args) {{ // Implementation goes here }}}}/** * Solution for Basic Calculator * Approach 3 */
function solve() {{ // Implementation goes here}}
module.exports = solve;/// Solution for Basic Calculator/// Approach 3
fn main() {{ println!("Solution implementation");}}package main
import "fmt"
// Solution for Basic Calculator// Approach 3
func main() {{ fmt.Println("Solution implementation")}}Interview Tips
Section titled “Interview Tips”When to use each approach
Section titled “When to use each approach”| Scenario | Approach | Reason |
|---|---|---|
| Interview asks for simplest solution | Stack with Sign | Easy to explain, minimal stack depth |
| Interview asks about scalability | Two-Pass Postfix | Generalizes to any precedence levels |
| Expression is deeply nested (1000+ levels) | Stack with Sign | O(depth) space is better than O(n) |
| Expression has mixed operators | Two-Pass Postfix | Shunting Yard handles precedence correctly |
| You need to extend to support variables/functions | Two-Pass Postfix | Easier to add features to tokenization and evaluation |