LeetCode #20 Easy

Valid Parentheses

Valid Parentheses is LeetCode 20 (Easy). You are given a string s made only of the six bracket characters (, ), [, ], { and }. Return true if the brackets are balanced and false otherwise.

A string counts as valid only when all three rules hold:

  • Same type: every opening bracket is closed by a bracket of the same type.
  • Right order: brackets close in the reverse of the order they opened, so pairs nest and never cross.
  • Nothing unmatched: every closing bracket has an opening bracket before it, and no opening bracket is still open at the end.

The string can be up to 10⁴ characters long, so the check should be one linear pass.

Constraints
  • 1 <= s.length <= 10⁴
  • s consists of parentheses only '()[]{}'.
stringstack
Open on LeetCode ↗
02

Intuition

Valid parentheses is a question about nesting. In {[()]} the ( opened last, so it must close first; the { opened first, so it closes last. The most recent unclosed bracket is always the next one to close.

That rule is exactly last in, first out, which is what a stack does. Each opener goes on top as the new innermost bracket, each closer must match the top, and a string is balanced only if nothing is left open at the end. Counting brackets is not enough: ([)] opens and closes one of each type, yet the pairs cross, and only the stack knows which bracket is innermost.

How to spot this pattern

Any time a later symbol has to match the most recent unmatched earlier symbol, reach for the balanced parentheses stack pattern: brackets, HTML tags, nested function calls, undo operations. With only one bracket type a single counter would do; several types need the stack to remember the order.

03

Approach

Try it first

Before reading on, list the three different ways a bracket string can be invalid, and say which line of your code catches each one. Aim for O(n) time.

1

Map each closer to its opener

Store pairs = { ')': '(', ']': '[', '}': '{' }. Keying by the closer means one lookup answers both questions: is this a closer? and which opener does it need?

2

Scan the string once

  • Opener (not a key in pairs) – push it.
  • Closer with a non-empty stack whose top equals pairs[ch] – pop.
  • Closer otherwise – return false at once.

The first closer that meets the wrong top, or an empty stack, already proves the string invalid, so there is no reason to keep scanning.

3

Check what is left

After the loop, return true only if the stack is empty. Anything still on it is an opener that was never closed; a string of only openers passes every closer check, so this final test is the only thing that catches it.

4

Why the stack is correct

A valid string can be reduced to empty by repeatedly deleting an adjacent pair like (). The stack performs exactly those deletions in left-to-right order: each pop removes an innermost pair. If a closer ever meets the wrong top, no sequence of deletions can fix it, because the bracket in between can never be removed first.

04

Valid Parentheses solution in Python | C++ | Java

▶1class Solution:
▶2 def isValid(self, s: str) -> bool:
▶3 pairs = {")": "(", "]": "[", "}": "{"}
▶4 stack = []
▶5 for ch in s:
▶6 if ch not in pairs:
▶7 stack.append(ch)
▶8 elif stack and stack[-1] == pairs[ch]:
▶9 stack.pop()
▶10 else:
▶11 return False
▶12 return not stack
s{0[1(2)3]4}5(6)7stackemptypairs)(][}{read once, left to right
stack[ ]open brackets still waiting
length8even
Idea. The most recent unclosed bracket must be the first one closed. That is last-in, first-out, so a stack holds the open brackets and its top is always the one the next closer has to match.
s{0[1(2)3]4}5(6)7stack{← toppairs)(][}{opener → push
s[0]'{'opening bracket
stack[{]size 1
An opener cannot be checked yet; its partner comes later. Push { so it waits on the stack.
s{0[1(2)3]4}5(6)7stack{[← toppairs)(][}{opener → push
s[1]'['opening bracket
stack[{ []size 2
Another opener, nested inside {. Push [ on top: it opened last, so it must be the first of the 2 open brackets to close.
s{0[1(2)3]4}5(6)7stack{[(← toppairs)(][}{opener → push
s[2]'('opening bracket
stack[{ [ (]size 3
Another opener, nested inside [. Push ( on top: it opened last, so it must be the first of the 3 open brackets to close.
s{0[1(2)3]4}5(6)7stack{[← top(popped ✓pairs)(][}{check(needs(top=match → pop the pair
s[3]')'closes '('
stack[{ []size 2
The top is (, exactly the opener ) needs. That pair is complete, so pop it. The bracket below becomes the new innermost one ([).
s{0[1(2)3]4}5(6)7stack{← top[popped ✓pairs)(][}{check[needs[top=match → pop the pair
s[4]']'closes '['
stack[{]size 1
The top is [, exactly the opener ] needs. That pair is complete, so pop it. The bracket below becomes the new innermost one ({).
s{0[1(2)3]4}5(6)7stack{popped ✓pairs)(][}{check{needs{top=match → pop the pair
s[5]'}'closes '{'
stack[]size 0
The top is {, exactly the opener } needs. That pair is complete, so pop it. The bracket below becomes the new innermost one; the stack is empty, so everything read so far is balanced.
s{0[1(2)3]4}5(6)7stack(← toppairs)(][}{opener → push
s[6]'('opening bracket
stack[(]size 1
An opener cannot be checked yet; its partner comes later. Push ( so it waits on the stack.
s{0[1(2)3]4}5(6)7stack(popped ✓pairs)(][}{check(needs(top=match → pop the pair
s[7]')'closes '('
stack[]size 0
The top is (, exactly the opener ) needs. That pair is complete, so pop it. The bracket below becomes the new innermost one; the stack is empty, so everything read so far is balanced.
s{0[1(2)3]4}5(6)7stackemptypairs)(][}{stack empty → return true
stack[]every opener was closed
resulttruereturn not stack
Valid. Every closer matched the innermost open bracket, and nothing is left waiting. An empty stack at the end is the second half of the test.
05

Common pitfalls

Returning true as soon as the loop ends

✗ Wrong
for ch in s:
    ...
return True
✓ Right
for ch in s:
    ...
return not stack

"((" never hits a mismatch, so the loop finishes cleanly. The leftover openers on the stack are the only sign it is invalid.

Reading the top of an empty stack

✗ Wrong
elif stack[-1] == pairs[ch]:
    stack.pop()
✓ Right
elif stack and stack[-1] == pairs[ch]:
    stack.pop()

")" or "())" reach a closer with nothing open. Without the emptiness check this raises an IndexError in Python and is undefined behaviour in C++.

06

Complexity

Time
O(n)
Space
O(n)
This valid parentheses LeetCode solution reads the string once: each character is pushed and popped at most once. The stack holds at most n openers, reached when the string is all opening brackets.
07

Valid Parentheses FAQ

How do you check valid parentheses using a stack?
  • Data structure: a stack of opening brackets, plus a map from each closer to its opener.
  • Opener: push it.
  • Closer: if the stack is empty or its top is not the matching opener, return false; otherwise pop.
  • End: return true only if the stack is empty.
  • Complexity: O(n) time, O(n) space.
  • Example: "{[()]}" pushes { [ (, then pops them in reverse order and ends empty, so it is valid.
How is the stack written in the valid parentheses Python solution?

A plain list: append pushes, pop() removes the top and stack[-1] peeks at it. not stack is true when it is empty, which is both the guard before peeking and the final answer.

Can valid parentheses be solved without a stack?

With only one bracket type, a counter works: add 1 for (, subtract 1 for ), fail if it goes negative, and require 0 at the end. With several types a counter cannot see crossings like ([)], so some form of stack is needed.

What are the ways a parentheses string can be invalid?

Three: a closer of the wrong type ((]), a closer with nothing open ()(), and openers left unclosed at the end (((). The first two are caught inside the loop; the third only by checking the stack is empty afterwards.

What is the time and space complexity of LeetCode 20?

O(n) time, because each character is pushed and popped at most once. O(n) space in the worst case, when every character is an opening bracket.