Valid Parentheses
A stack matches each closer to the most recent opener.
Approach
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.
Time complexity
O(n)
Space complexity
O(n)
Common mistake
Only counting brackets rather than matching types/order — '([)]' has balanced counts but is invalid.
Interactive walkthrough — step through it below, free and without an account
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
] appears while ( is still the most recent unmatched opener.Problem
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.
Problem and Goal
Learning goal: Recognize when a bracket sequence is valid and identify the information an algorithm must preserve.
Core idea A valid sequence closes brackets in the reverse order in which they were opened. This is not merely a counting problem: the algorithm must remember the most recent unmatched opener and compare each closer with it.
Algorithm plan
- Scan the string from left to right.
- Record every opening bracket.
- For a closing bracket, compare it with the latest recorded opener.
- Reject immediately if there is no opener or the types do not match.
- Accept only when the scan ends with no unmatched openers.
Micro-example
For ([{}]), the opening brackets are pushed as (, [, {.
The } must close { first, then ] closes [, and finally ) closes (.
Pitfalls
- Balanced counts do not guarantee valid nesting.
- A closer may appear before any opener.
- Leftover openers make the sequence invalid.
Recap
- Validity depends on both bracket type and order.
- The required memory is the sequence of unmatched openers.
- A stack is designed for this last-opened-first-closed behavior.
Problem and Goal
Validation contract
A bracket string is valid only when every closer matches the current stack top and the stack is empty at the end.
Check type and nesting order, not just total counts.
Key Idea: LIFO Nesting
Learning goal: Understand why last-in, first-out behavior matches nested brackets.
Core idea When a new opener appears inside an existing pair, it must be closed before the outer pair can close. Therefore, the newest opener is always the only legal candidate for the next closer. A stack exposes that candidate at its top in constant time.
Data structure model
| Operation | Meaning | Cost |
|---|---|---|
push(open) | Remember a new unmatched opener | O(1) |
peek() | Inspect the most recent opener | O(1) |
pop() | Remove a successfully matched opener | O(1) |
Key insight
Nested structure naturally becomes LIFO: the last bracket opened must be the first bracket closed.
Micro-example
After reading ([, the stack is [(, [] conceptually, with [ on top.
A ] is valid now; a ) is not, because it skips the inner pair.
Pitfalls
- Looking at the oldest opener reverses the required nesting rule.
- Popping before checking the type can hide a mismatch.
- Treating all bracket characters as interchangeable loses essential information.
Recap
- The stack top is the next opener that must be closed.
- Matching removes one completed pair.
- A mismatch proves the entire string is invalid.
Key Idea: LIFO Nesting
A closer is legal only if it matches the most recent opener.
Top `[` requires the next closer to be `]`.
Main Algorithm
Learning goal: Translate the stack idea into a precise one-pass procedure.
Core idea
Use a mapping from each closer to its required opener. On an opener, push it. On a closer, the stack must be nonempty and its top must equal the mapped opener; otherwise return false immediately. After processing all characters, return whether the stack is empty.
Algorithm plan
- Create an empty stack and a closer-to-opener map.
- For each character
ch:- If
chis an opener, push it. - Otherwise, check the stack top against its expected opener.
- If the check fails, return
false. - If it succeeds, pop the opener.
- If
- Return
stack.isEmpty().
for ch in s:
if ch is an opener:
push ch
else if stack is empty or top != matching opener for ch:
return false
else:
pop
return stack is emptyMicro-example
For ([{}]), each closer passes its top check, and each successful check pops one opener.
Pitfalls
- Check for an empty stack before reading its top.
- Return
falseon the first mismatch. - Do not return
truemerely because the scan completed.
Recap
- One scan performs all matching.
- Each character is pushed or popped at most once.
- The final empty-stack test catches missing closers.
Main Algorithm
| Approach | Tracks type? | Tracks order? | Verdict |
|---|---|---|---|
| Count each bracket | Yes, totals only | No | Fails on `([)]` |
| Stack matching | Yes | Yes, LIFO | Correct |
Walkthrough: A Complete Scan
Learning goal: Trace every stack change on a valid input from the problem examples.
Core idea
We will process ([{}]) left to right. Opening brackets create obligations, while closing brackets discharge exactly the obligation at the stack top. The stack contents therefore show the active nesting from outermost to innermost.
Data structure model
| Input event | Stack action | Required check |
|---|---|---|
| Opener | Push | None |
| Closer | Pop | Top matches its pair |
| End | Stop | Stack is empty |
Micro-example
The deepest point is after ([ {: the top is {.
The first closer is }, so it must pop {; the remaining pairs then close outward.
Why it works
Every successful pop removes the innermost completed pair, leaving the outer nesting intact.
Pitfalls
- A valid closer can still be invalid if the top has another type.
- The stack is ordered; displaying only its size hides the key evidence.
- Empty at the end means all obligations were discharged.
Recap
([{}])closes in the order},],).- Each closer matches exactly one stack top.
- The final empty stack establishes validity.
Walkthrough: A Complete Scan
End-to-end trace of the valid example `([{}])`.
(: push the outer opener.Pitfalls and Edge Cases
Learning goal: Distinguish true stack validation from tempting but incorrect shortcuts.
Core idea
Bracket counts answer only whether totals balance. They do not answer whether each closer arrives in the correct nesting position. For example, ([)] contains one matching pair of each type, but ] tries to close [ while ( is still on top, so the string is invalid.
Algorithm plan
- Reject a closer when the stack is empty.
- Reject a closer whose type differs from the top.
- Pop only after a successful match.
- At the end, reject any nonempty stack.
Micro-example
For ([)], the stack after ([ is [(, [].
The next character is ), but the top is [, so return false immediately.
Common pitfall
Only counting brackets misses ordering: ([)] has balanced counts but is invalid because the types are crossed.
Pitfalls
)at the beginning causes an empty-stack failure.((leaves unmatched openers.- A mismatch should not be repaired by searching deeper in the stack; nesting requires the top.
Recap
- Counts are necessary but not sufficient.
- The top mismatch is decisive.
- Empty input, if allowed, is valid because its stack is empty.
Pitfalls and Edge Cases
Why is `([)]` invalid?
Complexity and Wrap-up
Learning goal: Justify the efficiency of stack-based bracket validation and summarize the reusable pattern.
Core idea The algorithm makes one left-to-right pass. Each character is examined once, and every opening bracket is pushed at most once and popped at most once. The stack can grow to the full input length when many openers appear before their closers.
Algorithm plan
- Scan:
O(n)time. - Push and pop operations:
O(1)each. - Total auxiliary stack space:
O(n)worst case. - Final answer: valid exactly when no mismatch occurred and the stack is empty.
Micro-example
For (((( the stack reaches size 4, demonstrating the worst-case space usage.
For ()[]{} , the stack never holds more than one opener at a time.
Pitfalls
- Calling the solution
O(1)space because the bracket alphabet is fixed ignores the stack. - Forgetting the final emptiness check accepts strings with missing closers.
- A one-pass algorithm is still incorrect if it ignores bracket types.
Recap
- Time complexity:
O(n). - Space complexity:
O(n)in the worst case. - The reusable pattern is: push openers, match and pop closers, then require an empty stack.
Complexity and Wrap-up
Shows the cost of scanning and maintaining the stack.
Now try it on your own problem
Paste any LeetCode problem — or one of your own — and codebrew builds a walkthrough like this one for it.
Build a lesson from any problem
