Valid Parentheses
A stack matches each closer to the most recent opener.
Stack Trace
Problem
Given a string containing only parentheses and square or curly brackets, determine whether its brackets form a valid sequence. Every closing bracket must match the most recently opened bracket, and no opening bracket may remain unmatched at the end.
Examples
Input: "([{}])"
Output: true
{} first, then [], then ().Input: "([)]"
Output: false
) arrives while [ is the most recent unmatched opener.The Goal
Return True if every bracket in s is closed by the right type, in the right order, with nothing left open.
([{}]) is valid: } closes {, then ] closes [, then ) closes (. ([)] has one of each pair but isn't valid: ) comes while [ is still open.
Counting can't tell these apart. You have to know which opener is the latest one still open.
Close the latest first
Each closer matches the latest opener still open, and no opener is left at the end.
Type and order both matter.
Key Idea: The Latest Opener Closes First
Brackets nest, so the opener that came last has to close first. That's a stack: push each opener, and a closer has to match the top.
A match pops the top, and the opener under it is next in line. A closer that doesn't match the top, or comes when the stack is empty, settles it: the string is invalid.
A closer must close the top. A match pops it.
With [ on top, ] matches and ) doesn't.
The Algorithm
def is_valid(s):
pairs = {")": "(", "]": "[", "}": "{"}
stack = []
for c in s:
if c not in pairs:
stack.append(c)
elif not stack or stack[-1] != pairs[c]:
return False
else:
stack.pop()
return not stackpairs maps each closer to the opener it closes. Check that the stack isn't empty before reading its top, and return False at the first bad closer. The last line catches openers that never closed.
| Approach | Checks type | Checks order | On ([)] |
|---|---|---|---|
| Count each type | Yes | No | True, which is wrong |
| Stack of openers | Yes | Yes | False |
Walkthrough
Run it on ([{}]).
The three openers go on the stack, so { ends up on top. Then each closer finds its match there: } pops {, ] pops [, and ) pops (. The stack ends empty, so the answer is True.
Try Break it. On ([)], the stage stops at ) with [ on top and returns False.
Checkpoint · predict the next step
Step 6 of 17 on the stage: Read '{', an opener.
c on "{" (index 2) · stack = ["(", "["]
Pitfalls
- Check for an empty stack before reading its top.
)(fails at its first character. - Check the type, not that something is open. When
)comes in([)], the stack isn't empty, but its top is[. - Don't return
Truebecause the scan finished.((()ends with two openers still on the stack. - An empty string is valid: nothing is left open.
Why is ([)] invalid?
Complexity
Each character is read once, and each opener is pushed once and popped at most once, so time is O(n).
The stack can hold every character, as in ((((, so space is O(n). There are only six kinds of bracket, but the stack can still grow with n, so the space isn't O(1).
One scan, one stack
- Scan the stringEach character once.dominantO(n)
- Push and popEach opener goes on once and off at most once.O(1)
- The stackEvery character, at worst.dominantO(n)
| n | 10 | 100 | 1,000 | 10,000 |
|---|---|---|---|---|
| O(n) | 10 | 100 | 1K | 10K |
One pass, with a stack as deep as the nesting.
In short
Push every opening bracket. On a closing bracket, the top of the stack must be its matching opener — otherwise it's invalid. After the scan the stack must be empty. The stack naturally captures the last-opened-first-closed nesting.
Common mistake
Only counting brackets rather than matching types/order — '([)]' has balanced counts but is invalid.