LeetCode ยท #22

Generate Parentheses Solution

Given n pairs of parentheses, generate all combinations of well-formed parentheses.

01
Section One ยท Problem

Problem Statement

๐Ÿท๏ธ

Difficulty

Medium

๐Ÿ”—

LeetCode

Problem #22

๐Ÿ—๏ธ

Pattern

Backtracking โ€” constrained generation

Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses strings.

Example
Input: n = 3 Output: ["((()))", "(()())", "(())()", "()(())", "()()()"]
Constraints
โ€ข 1 โ‰ค n โ‰ค 8
02
Section Two ยท Approach 1

Brute Force โ€” Generate All & Filter

Generate all 2ยฒโฟ strings of length 2n using '(' and ')', then filter only valid ones. Validation uses a stack or counter โ€” scan left-to-right, increment on '(', decrement on ')'. Valid if counter never goes negative and ends at 0.

Problem: Generates 2ยฒโฟ candidates, most invalid. Backtracking prunes invalid branches early โ€” we never place a ')' unless there's an unmatched '(' to close. This reduces the search space to exactly the Catalan number Cโ‚™ valid strings.
MetricValue
TimeO(2ยฒโฟ ยท n) โ€” generate + validate
SpaceO(n) โ€” recursion depth
03
Section Three ยท Approach 2

Backtracking โ€” O(4โฟ/โˆšn)

Build the string character by character using two rules: (1) add '(' if we haven't used all n openers, (2) add ')' only if close < open (there's an unmatched opener to close). This guarantees every generated string is valid โ€” no filtering needed.

๐Ÿ’ก Mental model: Imagine building a staircase with n up-steps and n down-steps. You can always step up (add '(') if you haven't used all n ups. You can only step down (add ')') if you're above ground level โ€” meaning you've gone up more times than down. You never go below ground. Every path from start to end at ground level is a valid parenthesization.
Algorithm
  • Base case: When open == n and close == n โ†’ add current string to result.
  • Choice 1: If open < n โ†’ recursively try adding '('.
  • Choice 2: If close < open โ†’ recursively try adding ')'.
๐ŸŽฏ Why close < open?:
  • If we've placed 3 openers and 2 closers so far, there's 1 unmatched '(' โ€” so adding ')' is valid.
  • If close == open, there's nothing to close, so ')' would create an invalid prefix.
  • This single condition ensures validity without any post-filtering.
04
Section Four ยท Trace

Visual Walkthrough

Trace through n = 2:

Backtracking tree โ€” n=2 (showing open/close counts)
"" o=0 c=0 "(" o=1 c=0 "((" o=2 c=0 "()" o=1 c=1 "(()" o=2 c=1 "(())" โœ“ "()(" o=2 c=1 "()()" โœ“ Answer: ["(())", "()()"] โœ“
05
Section Five ยท Implementation

Code โ€” Java & Python

Java โ€” Backtracking
import java.util.*; class Solution { public List<String> generateParenthesis(int n) { List<String> result = new ArrayList<>(); backtrack(result, new StringBuilder(), 0, 0, n); return result; } private void backtrack(List<String> res, StringBuilder sb, int open, int close, int n) { if (sb.length() == 2 * n) { res.add(sb.toString()); return; } if (open < n) { sb.append('('); backtrack(res, sb, open + 1, close, n); sb.deleteCharAt(sb.length() - 1); // undo } if (close < open) { sb.append(')'); backtrack(res, sb, open, close + 1, n); sb.deleteCharAt(sb.length() - 1); // undo } } }
Python โ€” Backtracking
class Solution: def generateParenthesis(self, n: int) -> list[str]: result = [] def backtrack(current: str, open: int, close: int): if len(current) == 2 * n: result.append(current) return if open < n: backtrack(current + "(", open + 1, close) if close < open: backtrack(current + ")", open, close + 1) backtrack("", 0, 0) return result
06
Section Six ยท Analysis

Complexity Analysis

ApproachTimeSpaceTrade-off
Generate all + filterO(2ยฒโฟ ยท n)O(n)Most candidates invalid
Backtracking โ† optimal O(4โฟ/โˆšn) O(n) Only valid strings generated. Count = Catalan number.
Catalan number:
  • The number of valid parenthesizations of n pairs is the n-th Catalan number Cโ‚™ = (2n)! / ((n+1)! ยท n!).
  • For n=3: Cโ‚ƒ=5, for n=8: Cโ‚ˆ=1430. The bound O(4โฟ/โˆšn) comes from the asymptotic formula for Catalan numbers.
07
Section Seven ยท Edge Cases

Edge Cases & Pitfalls

CaseInputWhy It Matters
n = 11Only one string: "()".
n = 00Edge โ€” empty string [""] (not in constraints but worth knowing).
n = 88Maximum input โ†’ 1430 valid strings. Runs fast.
โš  Common Mistake: Forgetting to undo (backtrack) when using a mutable StringBuilder. After the recursive call returns, you must deleteCharAt to restore state. With Python's immutable strings (current + "("), this is automatic โ€” a new string is created each time.

โ† Back to Stack problems