#20EasyStack
Valid Parentheses
Start dumb: cancel pairs until nothing moves
The hands-on first idea: repeatedly delete every adjacent matched pair —
(), [], {} — until the string stops changing; valid means nothing is
left. It works, but each sweep is O(n) and can remove as little as one
pair, so the whole thing is O(n²) with string rebuilding on every
pass.
Find the bottleneck
All that rescanning is searching for something the scan already walked past: the most recently opened bracket. Brackets nest like function calls — last opened, first closed. The rescan re-discovers, over and over, a fact the algorithm could simply have remembered.
The optimal shape
One pass, one stack:
- Opener → push it; nothing about it can be decided yet.
- Closer → pop the top; if the types disagree (or the stack was empty),
return
falseon the spot — no future character can fix it. - After the scan, the stack must be empty: unclosed openers are as invalid as mismatched closers.
Each character is pushed and popped at most once — O(n) time,
O(n) stack space in the worst case ((((((). In the player, the
brackets still on the stack stay highlighted as the open “window”;
matched pairs retire to green.
Watch it think
Run the default, then the traps: the empty string (vacuously true), a
lone ) (closer with nothing open), ((( (the leftover-openers bug),
and ([)] (right types, wrong nesting).
Re-traces as you type — brackets only — ()[]{}, up to 32 chars; try the empty string, (((, ([)], or a stray ). Capped at 32 characters so the tiles stay readable.
Step 1 of 8: Brackets nest last-in-first-out — the most recent opener must close first — so an empty stack is the only honest starting memory. Variables: stack = ∅.
Data
Code
1fun isValid(s: String): Boolean {2 val pairs = mapOf(')' to '(', ']' to '[', '}' to '{')3 val stack = ArrayDeque<Char>()4 for (c in s) {5 if (c in pairs) {6 if (stack.removeLastOrNull() != pairs[c]) return false7 } else {8 stack.addLast(c)9 }10 }11 return stack.isEmpty()12}
State
- stack
- ∅
Why this step
Brackets nest last-in-first-out — the most recent opener must close first — so an empty stack is the only honest starting memory.
1 / 8
The same pattern also solves
Recognize the window, and these fall to the same skeleton.
- Generate Parentheses (LC 22) ↗
The constructive flip — backtracking builds every valid arrangement of n pairs instead of checking one.
- Longest Valid Parentheses (LC 32) ↗
The hardest pivot — return the length of the longest valid substring, via a stack of indices or DP.
- Minimum Add to Make Parentheses Valid (LC 921) ↗
Single bracket type — count the fewest insertions to balance, using the same unmatched-count idea.
- Valid Parenthesis String (LC 678) ↗
The interviewer's harder variant — a wildcard '*' breaks the plain stack and needs a min/max open-count range.