intermediate7 min read·Updated October 4, 2026

Generate Parentheses Explained: Tracing n = 3 Valid Combinations

Master Generate Parentheses with a complete walkthrough of n = 3 valid strings. Learn the backtracking mental model, recursion tree, and constraints.

By Learnisim AI·Published October 4, 2026
LEARNING ARCSetup → Model in the example → Full trace → Twist → Edge cases → Apply
PREREQUISITES
  • recursion basics
  • string manipulation
  • depth-first search
Generate Parentheses (n = 3) — Recursive Decision Tree & Pruning 1. The Combinatorial Trap 6 chars: 2^6 = 64 choices Most permutations are malformed Garbage Strings Pruned: )()() ( )))((( Closing precedes opening! The Two Structural Guards 1. Open Guard: Add '(' if openUsed < n (Max 3 open brackets total) 2. Close Guard: Add ')' if closeUsed < openUsed (Prevents dangling closes) 2. Recursive Decision Tree & State Trace (n = 3) (0, 0) : "" (1, 0) : "(" + '(' BLOCKED + ')' (close>=open) (2, 0) : "((" (1, 1) : "()" (3, 0) : "(((" (2, 1) : "(()" (3, 1) : "(()(" (()()) (3,2) (())() (3,2) All 5 Valid for n=3: ((())) (()()) (())() ()(()) ()()() Length = 2n = 6 Base case reached
All valid strings for n = 3 overview diagram
Why

Why simple nested brackets break standard combinatorics

Phase 1: The Combinatorial Trap

Imagine you need to generate every possible arrangement of matching parentheses for a given pair count . If you attempt this with a brute-force permutation generator, you quickly run into a wall of structural invalidity. For , there are 6 total characters (3 opening ( and 3 closing )), which yields binary choices or raw permutations depending on how you model the slots. Most of those combinations are completely malformed, such as )()()( or )))(((, where closing brackets appear before they have any matching openers.
Without a constrained generation strategy, your code wastes massive CPU cycles filtering out garbage strings after the fact. The core problem of generate parentheses is not just counting characters, but ensuring that at every single index along the string, the number of closing parentheses never exceeds the number of opening ones. We need a systematic way to build strings character by character so that illegal states are pruned before they ever materialize.
python
# Given: n = 3 pairs of parentheses
# Goal: Produce the complete list of 5 valid strings:
# ["((()))", "(()())", "(())()", "()(())", "()()()"]
# Question: Why does a naive permutation approach fail to scale for larger n?
Generate Parentheses (n = 3) : The Combinatorial Trap vs Pruning Phase 1: Naive Permutation (6! = 720 total) ))(()) (Invalid) )))((( (Orphan) )()()( (Premature) Wastes CPU Cycles Closes exceed opens at index: structural corruption. Only 5 out of 720 permutations are actually valid! Phase 2: Constrained Backtracking (n = 3) "" (0,0) "(" (1,0) pruned ( ) > ( "(( " (2,0) "((()))" (3,3) Invariant Check at Every Step: 1. open < n ➔ can add '(' 2. close < open ➔ can add ')' (Safe!) Zero invalid strings ever enter the recursion tree. O(4^n / sqrt(n)) Catalan complexity achieved. The 5 Canonical Valid Solutions for n = 3 ((())) (())() (()()) ()(()) ()()() Catalan Number C_3 = 5 unique combinations All bracket prefixes maintain open >= close count.
Why simple nested brackets break standard combinatorics diagram
Model

The Recursive Tree Model for Generate Parentheses

Phase 2: The Decision Tree Model

When we want to generate all valid strings for pairs of parentheses, we can visualize the process as walking down a decision tree. At every single step of building our string, we have a choice: do we append an open parenthesis ( or a close parenthesis )?
However, we cannot make these choices blindly, or we end up with invalid sequences like )( or (()))(. To make this rigorous, our mental model must track two running integers as state: openUsed (how many ( we have placed so far) and closeUsed (how many ) we have placed so far).
Instead of generating all possible bitstrings of length 6 (which is 64 combinations for ) and filtering them later, our recursive model prunes invalid branches instantly. We impose two strict structural guardrails on any path:
1. The Open Guard: We can only add an open parenthesis ( if our current openUsed count is strictly less than . For , we can place at most three ( characters total.
2. The Close Guard: We can only add a close parenthesis ) if our current closeUsed count is strictly less than openUsed. This ensures a ) never precedes its matching (.
Start: (open=0, close=0, path="")
├── Add '(' -> (open=1, close=0, path="(")
│ ├── Add '(' -> (open=2, close=0, path="((")
│ │ └── ...
│ └── Add ')' -> (open=1, close=1, path="()")
└── Add ')' -> BLOCKED (close >= open)
As we trace down this tree for , every time our path reaches a length of (meaning 3 opens and 3 closes), we have successfully constructed one of our target items, such as (())(). The model turns a messy combinatorial search into a clean, depth-first traversal of valid states.
Generate Parentheses (n = 3) — Recursive Decision Tree Model State & Strict Guardrails State Tracker openUsed, closeUsed, path 1. The Open Guard Can add '(' if openUsed < n (Max 3 opens allowed for n=3) 2. The Close Guard Can add ')' if closeUsed < openUsed (Prevents dangling ')' like )( ) Instant Pruning Skips all 2^(2n) = 64 brute bits, visiting only valid structures! Depth-First Search Tree (n = 3) Start: (0, 0) "" + '(' + ')' [X] (1, 0) "(" BLOCKED (0>=1) + '(' + ')' (2, 0) "((" (1, 1) "()" + '(' + ')' + '(' (3,0) "(((" (2,1) "(()" (2,1) "()(" ... continuing until length = 2n (6) ... Success Condition: length == 2n (open == 3 & close == 3) ((())) (())() ()(()) (())( +1 more
The Recursive Tree Model for Generate Parentheses diagram
Worked example

Step-by-Step Trace for n = 3 Parentheses

Phase 3: Working Through the n = 3 Decision Tree

To see our recursive tree model in action, let us trace the execution for . We maintain three variables in our function signature: openUsed (count of open brackets used so far), closeUsed (count of close brackets used so far), and path (the current string being constructed). When both openUsed and closeUsed equal , our path reaches length 6 and we collect it into our results array.

Given

- Target pair count:
- Initial state: openUsed = 0, closeUsed = 0, `path =
Step-by-Step Trace: n = 3 Parentheses Generation Initial State (n = 3) openUsed: 0 closeUsed: 0 path: " " (empty) Recursive Function Signature def generate(openUsed, closeUsed, path): • Add '(' if openUsed < n • Add ')' if closeUsed < openUsed [0, 0] " " [1, 0] "(" openUsed < 3 (Valid) [0, 1] Invalid closeUsed > openUsed [2, 0] "((" [1, 1] "()" Base Case Reached at Length 6 When openUsed == 3 & closeUsed == 3, collect path into results array!
Step-by-Step Trace for n = 3 Parentheses diagram
Practice

Predicting the Trace for n = 2 Parentheses

Phase 4: Practice

Now that you have seen how the algorithm builds all 5 valid strings for , it is time to test your mental model on a smaller instance. Consider running the same backtracking function with .
Recall the two core rules established in the recursive tree model:
- You may append an opening parenthesis ( if your current openCount < n.
- You may append a closing parenthesis ) if your current closeCount < openCount.
Take out a piece of paper or open a scratchpad and trace out the exact sequence of states (open, close, path) starting from (0, 0, "") until you hit the base length of .

The Task

1. Write down the complete list of valid strings generated when .
2. How many total leaves (both valid and invalid pruned branches) does the recursion tree visit for ?
3. Verify why a state like open = 1, close = 2 is immediately pruned by the second rule.
python
# Try tracing n = 2 manually or write a quick check:
# n = 2 expected output size is 2 strings.
Apply

Transferring Parenthesis Backtracking to Related Constraints

Phase 5: Transfer

Now that you have traced how the backtracking state machine builds the 5 valid strings for (from ((())) down to ()()()), you can apply this exact decision-tree pattern to kindred structural generation problems. The core rule we established—only taking branches that maintain structural validity at every step—remains identical when the alphabet or limits change.
Consider a variant where you must generate all valid combinations of pairs of mixed brackets, such as () and [], or a problem counting unique binary search trees with nodes, which follows the exact same Catalan number sequence. In each case, your mental model shifts from simple brute-force loops to managing explicit validity invariants during recursion.
When facing a new generation problem, ask yourself three questions derived from our backtracking model:
1. What constitutes a single incremental decision (like adding ()?
2.What inequality or state count defines an invalid branch that should be pruned immediately?
3.What defines a complete leaf node where the built string or structure is emitted?
By mapping these questions to your working knowledge of openUsed and closeUsed, you avoid writing redundant validation checks at the end of every recursion branch.
python
def generateParenthesisVariant(n):
    # TODO: Apply the open/close tracking pattern
    # to generate strings under a modified constraint.
    pass

FAQ

What is the exact output for n = 3 in Generate Parentheses?
For n = 3, the algorithm generates 5 valid strings: ["((()))", "(()())", "(())()", "()(())", "()()()"].
Why can't we just use standard combinatorics to generate parentheses?
Simple combinatorics would generate 2^(2n) total permutations of opening and closing brackets, but most of them are invalid because a closing bracket cannot appear before its matching opening bracket.
What are the two core tracking rules in the backtracking mental model?
We track the count of open parentheses used (must be less than n) and close parentheses used (must be strictly less than open count to maintain validity).
What is the time complexity of the Generate Parentheses algorithm?
The time complexity is tied to the nth Catalan number, O(4^n / √n), because we only explore valid branches in the recursion tree.

Keep learning