2Brute2Best
🔥0

Valid Parentheses

Easy·LeetCode #20Basic Anchor
LeetCode
30-Second Mental ModelStack — Matching / nesting
Complexity:O(N²)→O(N)
Key Insight

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.

Core Validity Invariant

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.

01

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.
Input

A string s of bracket characters.

Output

true if every bracket is matched correctly, otherwise false.

Example 1
Input: s = "()"
Output: true

One pair, opened and closed.

Example 2
Input: s = "()[]{}"
Output: true

Three pairs, one after another.

Example 3
Input: s = "(]"
Output: false

The ( is closed by a ], which is the wrong type.

Example 4
Input: s = "([])"
Output: true

The [] sits fully inside the (). The inner pair closes first, then the outer one.

Example 5
Input: s = "([)]"
Output: false

The ( is closed while the [ opened after it is still open. The pairs cross instead of nesting.

Constraints
  • • 1 ≤ s.length ≤ 10^4
  • • s contains only the characters ()[]{}
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 1.

    When I read a closing bracket, which open bracket is it supposed to close?

  2. 2.

    Is that always the first open bracket, or the most recent one that is still open?

  3. 3.

    Would counting how many of each type are open be enough? Try "([)]".

  4. 4.

    What should happen if a closing bracket arrives and nothing is open?

  5. 5.

    When I reach the end of the string, what must be true about the open brackets?

03

Brute force approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceRepeatedly delete "()", "[]", "{}" until nothing changesO(N²)O(N)
OptimalPush openers; each closer must match and pop the topO(N)O(N)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “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.
Pairs that open and close+inner pairs close before outer ones+the newest unmatched item is the only one that can match next→Stack — Matching / nesting

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).

10

Don't make this mistake

Forgetting to check the stack is empty at the end
✗ for (char c : s) { ... } return true;
✓ return st.empty();

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.

Popping or reading the top of an empty stack
✗ char top = st.top(); // when s starts with ')' st.pop();
✓ if (st.empty()) return false; char top = st.top(); st.pop();

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.

Using a counter instead of a stack (pattern confusion)
✗ int open = 0; for (char c : s) { if (c == '(' || c == '[' || c == '{') open++; else if (--open < 0) return false; } return open == 0;
✓ stack<char> st; // remember WHICH brackets are open, in order

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.

Comparing the closer with the first opener instead of the latest

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.

11

Want to implement this?

Write it yourself on LeetCode, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like Stack translate into production architectures and low-level memory mechanics.