Valid Parentheses
Read the string once. Every open bracket waits on a stack. A close bracket must match the bracket on top of the stack, which is the most recent one still open. If it matches, pop it: that pair is done. At the end, the stack must be empty.
A closer is allowed only if the stack is not empty and its top is the same type of opener. The whole string is valid only if every closer passed this test and the stack is empty at the end.
Problem
You are given a string s made only of the six bracket characters (, ), [, ], { and }.
Return true if the brackets are valid, otherwise false. The string is valid when:
- every open bracket is closed by a bracket of the same type,
- brackets are closed in the right order (the one opened last is closed first),
- every close bracket has an open bracket before it to match.
A string s of bracket characters.
true if every bracket is matched correctly, otherwise false.
One pair, opened and closed.
Three pairs, one after another.
The ( is closed by a ], which is the wrong type.
The [] sits fully inside the (). The inner pair closes first, then the outer one.
The ( is closed while the [ opened after it is still open. The pairs cross instead of nesting.
- • 1 ≤ s.length ≤ 10^4
- • s contains only the characters ()[]{}
First, think yourself
Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.
- 1.
When I read a closing bracket, which open bracket is it supposed to close?
- 2.
Is that always the first open bracket, or the most recent one that is still open?
- 3.
Would counting how many of each type are open be enough? Try "([)]".
- 4.
What should happen if a closing bracket arrives and nothing is open?
- 5.
When I reach the end of the string, what must be true about the open brackets?
Brute force approach
Optimized approach
Why does this algorithm work?
Dry run
All approaches compared
| Approach | Idea | Time | Space |
|---|---|---|---|
| Brute Force | Repeatedly delete "()", "[]", "{}" until nothing changes | O(N²) | O(N) |
| Optimal | Push openers; each closer must match and pop the top | O(N) | O(N) |
Why does this belong to this pattern?
- “closed by the same type”Each closer must be compared with a specific opener → we need to remember the openers, not just count them.
- “closed in the correct order”The last bracket opened must be the first one closed → last in, first out → stack.
- “every close bracket has a matching open”Check for an empty stack before popping, and for leftovers at the end.
This is the anchor of the sub-pattern: openers wait on the stack until their closer arrives, and a closer only ever looks at the top. Later questions keep this exact move but change what sits on the stack: the answer string itself (Remove Adjacent Duplicates), indices (Minimum Remove), or saved state for each level of nesting (Decode String).
Don't make this mistake
For "((" every character is an opener, so no check inside the loop ever fails. The two openers are still waiting at the end, so the answer must be false.
For ")" the stack is empty when the closer arrives. Calling top() or pop() on an empty std::stack is undefined behaviour and usually crashes. A closer with nothing open is simply invalid.
A single counter works when there is only one type of bracket. With three types it says yes to "([)]": the count goes 1, 2, 1, 0. Even one counter per type fails here, because the problem is the order: the ) arrives while [ is the most recent open bracket. Only a stack remembers that order.
Matching each closer with the earliest open bracket (like a queue) accepts "([)]" and rejects "([])". Inner brackets close first, so the partner is always the most recent opener: the top of the stack.
Want to implement this?
Write it yourself on LeetCode, then come back and compare your code with the solutions above.
Open coding workspaceHow this concept connects to System Design & C++ Systems
Top engineering interviews test whether you understand how algorithms like Stack translate into production architectures and low-level memory mechanics.