2Brute2Best
🔥0

Generate Parentheses

Medium·LeetCode #22Basic Anchor
LeetCode
30-Second Mental ModelConstraint satisfaction (place one item at a time, and prune as early as possible)
Complexity:O(n · 2^(2n))→O(n · Catalan(n))
Key Insight

Keep two counters, open and close. Add ( while open < n. Add ) only when close < open, which means an unmatched ( is waiting for it. Then every prefix stays balanced, and every string that reaches length 2n is an answer.

Core Validity Invariant

A prefix is valid when close never goes above open and open never goes above n. Before each ), the rule to check is close < open, because only an unmatched ( can take it.

01

Problem

You are given a whole number n. Write out every string that uses exactly n opening brackets ( and n closing brackets ), and is well-formed.

Well-formed means two things. Reading from the left, the count of ( is never lower than the count of ). And at the end both counts are equal.

Return all such strings. The order does not matter.

Input

An integer n.

Output

A list of every well-formed string that has n pairs of brackets.

Example 1
Input: n = 3
Output: ["((()))", "(()())", "(())()", "()(())", "()()()"]

There are five ways to arrange three pairs. In each one, no prefix has more ) than (.

Example 2
Input: n = 1
Output: ["()"]

With one pair, the only well-formed string is ().

Constraints
  • • 1 ≤ n ≤ 8
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.

    Each answer is a string of length 2n. If every place could be ( or ), how many strings would that give?

  2. 2.

    Is there a string that can never become well-formed, no matter what comes after it? Look at the start of the string.

  3. 3.

    While I build the string, how do I know how many ( are still waiting for a )?

  4. 4.

    Can a string start with )? What does that do to the count?

  5. 5.

    If I only allow choices that keep the string valid, will I ever reach a dead end and have to undo a choice?

03

Brute force approach

04

Better approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceBuild all 2^(2n) strings of brackets, keep the balanced onesO(n · 2^(2n))O(n)
BetterBuild only strings with exactly n `(` and n `)`, then check balanceO(n · C(2n, n))O(n)
OptimalBuild left to right; allow `(` while open < n and `)` only while close < openO(n · Catalan(n))O(n)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “generate all strings”The answer is a list of complete candidates, built by choices. That is a search, not a count.
  • “well-formed”A rule on every prefix: a `)` can only close a `(` that is still open.
  • “n pairs”The length is fixed at 2n, and each bracket is used exactly n times. Stop when the string is complete.
Build one character at a time+keep two counters (open, close)+allow a choice only if the rule still holds+stop at 2n characters→Constraint satisfaction (place one item at a time, and prune as early as possible)

The rule is close < open, checked before each ). Unlike N-Queens or Sudoku, nothing has to be scanned: two counters are the whole state, so each check costs O(1). What makes it different from its neighbours is that every branch the rule allows leads to an answer, so the search never runs into a dead end and never needs to undo a wasted branch.

10

Don't make this mistake

Pattern confusion: allowing ')' while close < n
✗ if (close < n) { cur.push_back(')'); build(cur, open, close + 1, n, out); cur.pop_back(); }
✓ if (close < open) { cur.push_back(')'); build(cur, open, close + 1, n, out); cur.pop_back(); }

The count rule (close < n) belongs to the better approach, where strings are only counted. Used here, it lets a ) come first. On n = 2 it returns 6 strings instead of 2, and four of them are not balanced: ())(, )((), )()( and ))((. The check must compare with open, the number of ( so far.

Forgetting pop_back on the '(' branch
✗ if (open < n) { cur.push_back('('); build(cur, open + 1, close, n, out); }
✓ if (open < n) { cur.push_back('('); build(cur, open + 1, close, n, out); cur.pop_back(); }

The '(' that was added is never removed, so later strings carry it. On the dry-run input n = 3 this returns 4 strings instead of 5. ((())) is right, but ((()() appears twice and ((())( is not balanced.

Dropping the open < n check
✗ cur.push_back('('); build(cur, open + 1, close, n, out); cur.pop_back(); // no open < n test
✓ if (open < n) { cur.push_back('('); build(cur, open + 1, close, n, out); cur.pop_back(); }

Without the test, ( keeps being added past n. On n = 3 the rest of the rules still hold, so the output has 20 strings, not 5, and the first one is ((((((, which has six ( and no ).

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 Backtracking translate into production architectures and low-level memory mechanics.