Valid Parentheses
Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid. An input string is valid if: open brackets are closed by the same type of brackets, open brackets are closed in the correct order, and every close bracket has a corresponding open bracket of the same type.
- 1 <= s.length <= 10⁴
- s consists of parentheses only: ()[]{}
- A valid string always has even length (every open needs one close)
s = "()"trues = "()[]{}"trues = "{[()]}"trues = "(]"falseA first instinct is to count. If the string has as many ( as ), isn't it balanced? Try ([)]. Two opens, two closes, the counts are perfectly equal — yet it is nonsense: the ( ends up closed by ]. Counting throws away the one thing that actually decides this problem: order — specifically, which bracket is still open right now.
Look at what a valid string really demands. When you hit a closing bracket, it can only legally close the opening bracket that is still unmatched and was opened most recently. Read { [ ( and then a ) arrives: the only opener it may match is the (, because that's the newest one you haven't closed. Everything older is frozen behind it — you cannot touch the { until the [ and ( sitting inside it are closed first.
So as you scan left to right, you need a running memory of "brackets opened but not yet closed", and you always deal with the newest one first: add to one end, remove from that same end, newest on top. That structure has a name — a stack.
A stack is just a pile you can only touch from the top. Two moves: push (put one on top) and pop (take the top one off). The last thing pushed is the first thing popped — "Last In, First Out", or LIFO. A stack of plates works the same way: you always take the top plate, never reach into the middle.
Here is the whole method. Walk the string one character at a time:
- An opening bracket → push it. You've just recorded a promise to close it later.
- A closing bracket → look at the top of the stack. If it's the matching opener, pop it (promise kept). If it isn't — wrong type, or the stack is empty — the string is invalid; stop.
At the very end the stack must be empty. Anything still on it is an opener that was never closed.
def is_valid(s):
match = {')': '(', ']': '[', '}': '{'}
stack = []
for ch in s:
if ch in match: # a closing bracket
if not stack or stack.pop() != match[ch]:
return False # closes nothing, or wrong type
else: # an opening bracket
stack.append(ch)
return not stack # valid only if nothing left open) or (]}). A closing bracket whose top doesn't match means a type clash ((]). And a non-empty stack at the end means an opener was never closed ((((). The first two live in the closing branch above; the third is the final return not stack. Drop any one of them and specific inputs slip through.( — opener, push. Stack: [ ( ][ — opener, push. Stack: [ (, [ ]) — closer. Top is [, but ) needs (. Mismatch → return False.[, and ) cannot close a [. The stack caught exactly what counting missed.This tiny problem is the cleanest example of the one situation a stack was built for: whenever the most recently seen, still-unfinished thing is the one you must handle first. Notice the shape of the reasoning — each opener created an obligation, obligations had to be resolved newest-first, and older ones waited underneath, untouched. Any problem with that shape wants a stack.
Signals to watch for in an unfamiliar problem statement, each tied to that newest-first shape:
- Matching or pairing things that can nest — brackets, tags, quotes.
- The words "nested", "innermost first", "balanced", "valid order".
- Undo / go back one step — the last action is the first to reverse (you'll see this in Simplify Path, where .. cancels the last folder).
- Deferred evaluation — you can't finish the current thing until a newer, inner thing finishes first (Evaluate RPN, Decode String, Basic Calculator II all live here).
The invariant to keep in your head — and the thing to look for in any candidate problem — is this: the stack always holds exactly the currently-open, not-yet-resolved items, oldest at the bottom, newest on top. If you can describe your problem's "open items" that way, the stack writes itself; the only decisions left are what you push, when you pop, and what counts as a "match".
And the boundary: if the thing you must handle next is the oldest waiting item instead of the newest, a stack is the wrong tool — you want a queue (First In, First Out). Serving people in arrival order or exploring a graph breadth-first flips the rule.
Last-In-First-Out Verification
Start with an empty stack.
A stack naturally ensures that the most recently opened context is the first to be closed.