Stacks and Queues · Stacks and Queues Fundamentals

Stack Matching Validation

Using a stack to confirm that every "opener" in a sequence is closed by the correct matching "closer," in the correct nested order, before anything else closes it.

  • 5 min read
  • 7 practice questions
  • 2 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: push indices, not characters
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

Brackets in code, tags in HTML, parentheses in a formula: every opener must be closed by the matching closer, and the most recently opened one must close first. Counting openers and closers isn't enough - "([)]" has balanced counts and is still wrong. A stack records exactly the order that matters.

This skill covers bracket matching with a stack: push openers, check each closer against the top, and require an empty stack at the end. It also covers pushing indices instead of characters, which measures the longest valid stretch.

We'll go from simple to more technical:

  1. The pattern: push openers, match each closer with the most recent one, finish empty.
  2. A second flavor: push indices with a base, to find the longest valid substring.
  3. Where matching shows up (editors, parsers, compilers, undo histories).
  4. What breaks it: popping empty, counting instead of stacking, the final check, contiguity.

The approach

Java

boolean isValid(String s) {
    Deque<Character> stack = new ArrayDeque<>();
    for (char c : s.toCharArray()) {
        if (c == '(' || c == '[' || c == '{') {
            stack.push(c);
        } else {
            if (stack.isEmpty()) return false;              // a closer with nothing open
            char open = stack.pop();                        // it must match the MOST RECENT opener
            if ((c == ')' && open != '(') || (c == ']' && open != '[') || (c == '}' && open != '{')) return false;
        }
    }
    return stack.isEmpty();                                 // anything left was never closed
}

Python

def is_valid(s: str) -> bool:
    stack = []                                      # a list is Python's stack: append / pop / [-1]
    for c in s:
        if c in "([{":
            stack.append(c)
        else:
            if not stack:
                return False                        # a closer with nothing open
            open_ = stack.pop()                     # it must match the MOST RECENT opener
            if (c == ")" and open_ != "(") or (c == "]" and open_ != "[") or (c == "}" and open_ != "{"):
                return False
    return not stack                                # anything left was never closed

Three checks, three distinct bugs if one is missing: a closer arriving at an empty stack (")"), a closer that doesn't match the top ("([)]"), and openers still waiting at the end ("(("). ArrayDeque is the stack to use; java.util.Stack is a legacy synchronized class built on Vector, and its own documentation recommends Deque instead. In Python a plain list is the stack (append, pop, stack[-1] to peek); queue.LifoQueue is the synchronized one, meant for passing work between threads.

A second flavor: push indices, not characters

"How long is the longest valid (well-formed, contiguous) substring?" Push each '(' index, and keep one extra index at the bottom: the position just before the current valid run.

Java

int longestValidParentheses(String s) {
    Deque<Integer> stack = new ArrayDeque<>();
    stack.push(-1);                                   // base: just before the current valid run
    int best = 0;
    for (int i = 0; i < s.length(); i++) {
        if (s.charAt(i) == '(') {
            stack.push(i);
        } else {
            stack.pop();                              // match the latest '(' (or drop the base)
            if (stack.isEmpty()) stack.push(i);       // an unmatched ')' becomes the new base
            else best = Math.max(best, i - stack.peek());
        }
    }
    return best;
}

Python

def longest_valid_parentheses(s: str) -> int:
    stack = [-1]                                    # base: just before the current valid run
    best = 0
    for i, c in enumerate(s):
        if c == "(":
            stack.append(i)
        else:
            stack.pop()                             # match the latest '(' (or drop the base)
            if not stack:
                stack.append(i)                     # an unmatched ')' becomes the new base
            else:
                best = max(best, i - stack[-1])
    return best

After a match, the top of the stack is the index just before the valid stretch ending at i, so its length is i - stack.peek(). Counting matched pairs instead gives the total, not the longest contiguous stretch: "()(()" has 2 pairs but its longest valid substring is 2, not 4. Measuring only from the matched '(' misses runs that join: "()(())" is 6, not 4.

Where you'll see it

Editors highlight the matching bracket and flag unclosed ones; JSON, XML and HTML parsers track open elements on a stack; compilers check nested blocks; and "undo" histories are stacks of the most recent actions.

Which tool for which job?

You needUse
Valid or not, several bracket typesa stack of openers
Valid or not, one bracket typea depth counter
The longest valid substringa stack of indices with a base, or two counter passes
The fewest removals to make it validone counter pass
Deeply nested inputan explicit stack, not recursion

Common mistakes

MistakeWhat happensDo this instead
Popping an empty stackNoSuchElementException, or IndexError in Pythoncheck for empty first: a closer with nothing open is invalid
Counting openers and closers"([)]" passesa stack, so order is checked
Skipping the final check"((" passesthe stack must end empty
Counting pairs for the longest stretch"()(()" reports 4 instead of 2indices with a base
java.util.Stacka legacy synchronized classArrayDeque

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. Why isn't counting openers and closers of each type enough?

Show answer

Counts ignore order. "([)]" has one of each opener and closer, but ')' closes '[' - the most recent opener - which is invalid. Only a stack checks that each closer matches the latest unclosed opener.

2. What must happen when a closer arrives and the stack is empty?

Show answer

Return false - there's nothing to close. Calling pop() anyway throws NoSuchElementException on an ArrayDeque.

3. In longest valid parentheses, why keep a base index at the bottom of the stack?

Show answer

After a match, the top of the stack must be the index just before the current valid stretch, so its length is i - stack.peek(). The base (initially -1, then each unmatched ')') marks where the next valid stretch can start.

Key takeaways

  • Push openers; each closer must match the top; the stack must end empty.
  • Check for an empty stack before popping.
  • Use ArrayDeque, not java.util.Stack.
  • Push indices with a base to measure the longest valid stretch.

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
Several bracket typesstack of openers
One bracket typedepth counter
Longest valid stretchindices + base, or two counter passes
Fewest removalsone counter pass
Deep nestingexplicit stack

Snippets worth knowing by heart

Java

Deque<Character> stack = new ArrayDeque<>();
for (char c : s.toCharArray()) {
    if ("([{".indexOf(c) >= 0) stack.push(c);
    else if (stack.isEmpty() || "([{".indexOf(stack.pop()) != ")]}".indexOf(c)) return false;
}
return stack.isEmpty();

Python

open_ = removed = 0                      # fewest removals to make it valid
for ch in s:
    if ch == "(":
        open_ += 1
    elif open_:
        open_ -= 1
    else:
        removed += 1
answer = removed + open_

The traps

  • Popping empty: a closer with nothing open throws. Check for empty first; that input is invalid.
  • Counting instead of stacking: "([)]" passes when you only count. Use a stack so order is checked.
  • The final empty check: "((" passes without it. The stack must end empty.
  • Pair counts for the longest stretch: "()(()" reports 4 instead of 2. Track indices with a base.

Staff-level points, one line each

  • One bracket type: two counter passes give the longest valid stretch in O(1) space; they matched the stack on 2,000 strings.
  • Length 16: 12,870 balanced, 1,430 valid (Catalan(8)), a 1/(n + 1) share.
  • Fewest removals: unmatched ')' plus leftover '('; it matched brute force on 300 strings.
  • 100,000 nested pairs: StackOverflowError in Java, RecursionError in Python; an explicit stack is fine.

Say it in an interview

  • "Each closer has to match the most recent unclosed opener, which is a stack."
  • "Three checks: empty on a closer, a mismatch, and leftovers at the end."
  • "With one bracket type I can use a counter."
  • "For deep input I'd use an explicit stack, not recursion."

Practice

This skill comes with 7 multiple-choice questions that tell you which misconception a wrong answer reveals and 2 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Stack Matching Validation free

Valid Parentheses

EasyFreeCoding problemJava · Python

Check whether the brackets in s are properly matched. s uses only the characters ( ) [ ] { }. Every opening bracket needs a closing bracket of the same kind, and pairs must nest: whatever opens last closes first.

Longest Valid Parentheses

HardProCoding problemJava · Python

s is made of ( and ) only. Find the longest stretch of consecutive characters in which the parentheses balance and close in order, and return its length.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Stack Matching Validation free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime