#227MediumStack
Basic Calculator II
Start dumb: evaluate left to right
The tempting first idea is to just walk the expression and apply each operator
as you meet it. But 3 + 2 * 2 becomes (3 + 2) * 2 = 10, not 7 —
left-to-right ignores that * and / bind tighter. The next fix is two
passes: first sweep through resolving every * and /, then add and subtract
what’s left. It’s correct but clumsy, rewriting the sequence as it goes.
Find the bottleneck
The only reason * and / need special handling is precedence: they bind to
the number right next to them, so they can’t wait. + and -, on the other
hand, can wait — their contribution never overrides anything earlier. So you
don’t need a second pass; you need to defer the additions.
The optimal shape
Track the number you’re reading and op, the operator sitting in front of
it (it starts as an implicit +). Each time you reach the next operator — or
the end of the string — apply op to the number you just finished:
+→ pushnumber.-→ push-number.*→ pushpop() * number./→ pushpop() / number, truncated toward zero.
Then remember the new operator and reset the number. Sum the stack to finish.
O(n) time, O(n) space. In the player, * and / visibly reach back
and rewrite the top of the stack, while + and - just drop a term on top.
Watch it think
Run the default 3+2*2 and watch 2*2 pop the 2 and drop 4 in its place.
Then try 14-3/2 (truncation toward zero), 100/10/2 (left-associative
division), and 3+5 / 2 (spaces around a number change nothing).
Re-traces as you type — digits with + - * / and spaces, no parentheses — up to 32 chars; try 14-3/2, 100/10/2, or 3+5 / 2. Capped at 32 characters so the tiles stay readable.
Step 1 of 7: No number has been read yet, and the operator in front of the very first number is an implicit +, so the stack of terms-to-be-summed starts empty. Variables: stack = ∅, number = 0, op = '+'.
Data
Code
1fun calculate(s: String): Int {2 val stack = ArrayDeque<Int>()3 var number = 04 var op = '+'5 for ((i, c) in s.withIndex()) {6 if (c.isDigit()) number = number * 10 + (c - '0')7 if (!c.isDigit() && c != ' ' || i == s.lastIndex) {8 when (op) {9 '+' -> stack.addLast(number)10 '-' -> stack.addLast(-number)11 '*' -> stack.addLast(stack.removeLast() * number)12 '/' -> stack.addLast(stack.removeLast() / number)13 }14 op = c15 number = 016 }17 }18 return stack.sum()19}
State
- stack
- ∅
- number
- 0
- op
- '+'
Why this step
No number has been read yet, and the operator in front of the very first number is an implicit +, so the stack of terms-to-be-summed starts empty.
1 / 7
The same pattern also solves
Recognize the window, and these fall to the same skeleton.
- Basic Calculator (LC 224) ↗
Parentheses and unary minus but no * / — a stack of saved (result, sign) contexts instead of pending terms.
- Basic Calculator III (LC 772) ↗
Both worlds at once — * / precedence and parentheses — so you carry both a term stack and a context stack.
- Evaluate Reverse Polish Notation (LC 150) ↗
Precedence already resolved into postfix, so a single operand stack evaluates it with no operator bookkeeping.
- Different Ways to Add Parentheses (LC 241) ↗
Turns precedence inside out — enumerate every grouping via divide and conquer instead of fixing one order.