Loading problems...
TL;DR: By utilizing a stack to store additive terms and resolving multiplicative operations immediately upon discovery, we can evaluate the expression in a single linear pass.
The LeetCode 227 problem, known as Basic Calculator II, asks us to evaluate a string expression containing non-negative integers and the operators +, -, *, and /. The evaluation must respect the standard order of operations (operator precedence), meaning multiplication and division must be performed before addition and subtraction. We are not allowed to use built-in evaluation functions like eval().
A naive or brute force approach to solving Basic Calculator II often involves recursively searching for the operator with the lowest precedence to split the expression.
+ or -.+ or - exists, scan for * or / and split similarly.textfunction solve(expression): if expression is just a number: return parsed number // Find split point (lowest precedence, rightmost) idx = find_last_plus_or_minus(expression) if idx != -1: return solve(left_part) + solve(right_part) (or -) idx = find_last_mul_or_div(expression) return solve(left_part) * solve(right_part) (or /)
Time Complexity: . In the worst case (e.g., 1+2+3+...), the algorithm scans the string linearly to find the split point, performs the split, and recurses. This repeated scanning leads to quadratic time complexity.
Why it fails: While semantically correct, this approach effectively constructs an Abstract Syntax Tree (AST) implicitly through recursion. For constraints where string length s is up to , an solution will result in a Time Limit Exceeded (TLE) error.
The primary challenge in LeetCode 227 is handling operator precedence without parentheses. We cannot simply process the expression from left to right because a + operation cannot be executed until we ensure the right-hand operand isn't part of a subsequent * or / operation (e.g., in 3 + 2 * 5, we cannot add 3 and 2 immediately).
The core insight is to treat the expression as a sum of terms. We can use a stack to store these terms.
Visual Description:
Imagine the stack as a list of numbers waiting to be summed. As we iterate through 3 + 2 * 5:
3, push 3. Stack: [3].+, note it.2. Previous op was +, so push 2. Stack: [3, 2].*, note it.5. Previous op was *. We don't push 5. Instead, we pop 2, calculate 2 * 5 = 10, and push 10. Stack: [3, 10].3 + 10 = 13.
Consider the input s = "3+2*2".
stack = [], lastOp = '+', currNum = 0.currNum becomes 3.lastOp (+). Push 3 to stack. stack = [3].lastOp to +. Reset currNum to 0.currNum becomes 2.lastOp (+). Push 2 to stack. stack = [3, 2].lastOp to *. Reset currNum to 0.currNum becomes 2.lastOp (*).2). Calculate 2 * 2 = 4.4. stack = [3, 4].3 + 4 = 7.The algorithm correctly implements operator precedence by ensuring that multiplication and division bind tighter than addition and subtraction.
* or /, we effectively "merge" the current number with the last term on the stack. This prevents the previous term from being added to the overall sum until the higher-precedence operation is complete.+ or -, we "seal" the previous number as a complete term and push it, making it available for future addition.(a + b) + c = a + (b + c), deferring the sum until the end produces the correct result.cppclass Solution { public: int calculate(string s) { stack<int> myStack; char lastOp = '+'; int currentNumber = 0; int n = s.length(); for (int i = 0; i < n; ++i) { char c = s[i]; if (isdigit(c)) { currentNumber = currentNumber * 10 + (c - '0'); } // Trigger processing if current char is an operator or it's the last character. // Note: We skip spaces unless it's the last character. if ((!isdigit(c) && !isspace(c)) || i == n - 1) { if (lastOp == '+') { myStack.push(currentNumber); } else if (lastOp == '-') { myStack.push(-currentNumber); } else if (lastOp == '*') { int top = myStack.top(); myStack.pop(); myStack.push(top * currentNumber); } else if (lastOp == '/') { int top = myStack.top(); myStack.pop(); myStack.push(top / currentNumber); } lastOp = c; currentNumber = 0; } } int result = 0; while (!myStack.empty()) { result += myStack.top(); myStack.pop(); } return result; } };
javaclass Solution { public int calculate(String s) { if (s == null || s.length() == 0) return 0; Deque<Integer> stack = new ArrayDeque<>(); int currentNumber = 0; char lastOp = '+'; int n = s.length(); for (int i = 0; i < n; i++) { char c = s.charAt(i); if (Character.isDigit(c)) { currentNumber = currentNumber * 10 + (c - '0'); } if ((!Character.isDigit(c) && c != ' ') || i == n - 1) { if (lastOp == '+') { stack.push(currentNumber); } else if (lastOp == '-') { stack.push(-currentNumber); } else if (lastOp == '*') { stack.push(stack.pop() * currentNumber); } else if (lastOp == '/') { stack.push(stack.pop() / currentNumber); } lastOp = c; currentNumber = 0; } } int result = 0; for (int num : stack) { result += num; } return result; } }
pythonclass Solution: def calculate(self, s: str) -> int: stack = [] current_number = 0 last_op = '+' n = len(s) for i, char in enumerate(s): if char.isdigit(): current_number = current_number * 10 + int(char) # If current char is operator or last char in string if (not char.isdigit() and char != ' ') or i == n - 1: if last_op == '+': stack.append(current_number) elif last_op == '-': stack.append(-current_number) elif last_op == '*': top = stack.pop() stack.append(top * current_number) elif last_op == '/': top = stack.pop() # Python // floors, but problem requires truncation toward zero # e.g., -3 // 2 = -2, but we want -1 stack.append(int(top / current_number)) last_op = char current_number = 0 return sum(stack)
1+2+3+4), the stack stores all numbers from the expression.The Stack - Expression Evaluation pattern is highly versatile and appears in several popular interview questions.
() and +/-. Requires handling nested contexts, often by pushing the current local sum onto the stack when ( is found.*, /) and parentheses (). This requires a robust application of the stack pattern or conversion to RPN.Why does my solution fail on inputs like 3+2*2?
3 + 2 immediately before seeing the *.2 until you know the subsequent operator is not * or /.Why do I get errors with negative division in Python?
// for division.// operator performs floor division (rounds down to negative infinity), whereas C++/Java and the problem statement require truncation toward zero. is , but is .-3 // 2-2int(-3 / 2)-1Why is the last number in the string ignored?
if operator block.|| i == n - 1 is crucial.Why does 42 parse as 4 and 2?
currentNumber or pushing to stack on every digit.num = num * 10 + digit.