title: Generate Parentheses
slug: generate-parentheses
difficulty: medium
leetcode_id: 22
leetcode_url: https://leetcode.com/problems/generate-parentheses/
categories:
  - strings
  - recursion
patterns:
  - backtracking

function_signature: "def generate_parenthesis(n: int) -> list[str]:"

test_cases:
  visible:
    - input: { n: 3 }
      expected: ["((()))", "(()())", "(())()", "()(())", "()()()"]
    - input: { n: 1 }
      expected: ["()"]
  hidden:
    - input: { n: 2 }
      expected: ["(())", "()()"]
    - input: { n: 4 }
      expected: ["(((())))", "((()()))", "((())())", "((()))()", "(()(()))", "(()()())", "(()())()", "(())(())", "(())()()", "()((()))", "()(()())", "()(())()", "()()(())", "()()()()"]

description: |
  Given `n` pairs of parentheses, write a function to *generate all combinations of well-formed parentheses*.

  A well-formed parentheses string has equal numbers of opening and closing parentheses, with every closing parenthesis matching a preceding opening one.

constraints: |
  - `1 <= n <= 8`

examples:
  - input: "n = 3"
    output: '["((()))","(()())","(())()","()(())","()()()"]'
    explanation: "All 5 valid combinations of 3 pairs of parentheses."
  - input: "n = 1"
    output: '["()"]'
    explanation: "With just one pair, there's only one valid combination."

explanation:
  intuition: |
    Imagine you're building a string character by character, and at each step you can choose to add either an opening `(` or a closing `)` parenthesis.

    The key insight is that not every choice is valid. A string of parentheses is **well-formed** if at any point while reading left-to-right, the number of closing parentheses never exceeds the number of opening ones. Think of it like a balance: each `(` adds +1 to the balance, and each `)` subtracts 1. The balance must never go negative.

    This naturally leads to a **decision tree** approach. At each position, we branch based on what characters we *can* legally add:
    - We can add `(` if we haven't used all `n` opening parentheses yet
    - We can add `)` if we have more opening parentheses than closing ones (i.e., there's an unmatched `(` to close)

    By exploring all valid paths through this decision tree, we generate exactly the set of well-formed parentheses strings — no more, no less.

  approach: |
    We solve this using **Backtracking** — systematically building candidates and abandoning paths that can't lead to valid solutions.

    **Step 1: Define the recursive state**

    - `current`: The string we're building
    - `open_count`: Number of `(` parentheses used so far
    - `close_count`: Number of `)` parentheses used so far

    &nbsp;

    **Step 2: Identify base case**

    - When `len(current) == 2 * n`, we've placed all parentheses
    - Add the completed string to our results list

    &nbsp;

    **Step 3: Define recursive choices**

    - **Add `(`**: Only if `open_count < n` (we haven't used all opening parentheses)
    - **Add `)`**: Only if `close_count < open_count` (there's an unmatched `(` to close)

    &nbsp;

    **Step 4: Backtrack after each choice**

    - After exploring a path, the recursion naturally "unwinds"
    - Since we pass strings (immutable in Python), backtracking is implicit
    - With mutable structures, you'd explicitly remove the last character

    &nbsp;

    The constraints on when we can add each character ensure we only generate valid combinations, making this more efficient than generating all permutations and filtering.

  common_pitfalls:
    - title: Generating All Permutations Then Filtering
      description: |
        A naive approach might generate all possible strings of `(` and `)` characters, then filter for valid ones.

        With `n = 8`, that's `2^16 = 65,536` strings to generate and validate, but only `1,430` are valid (the 8th Catalan number). This wastes significant computation on invalid strings.

        The backtracking approach only explores valid paths, never generating invalid strings in the first place.
      wrong_approach: "Generate all 2^(2n) strings, filter valid ones"
      correct_approach: "Use constraints during generation to only build valid strings"

    - title: Forgetting the Close Constraint
      description: |
        It's tempting to think you can always add a `)` as long as you haven't used all `n` of them. But consider building with `n = 2`:

        Starting with `()`, if you add `)` next you get `())` — this is invalid because the third character closes a parenthesis that was never opened.

        The rule is: you can only add `)` when `close_count < open_count`, not just when `close_count < n`.
      wrong_approach: "Add ) whenever close_count < n"
      correct_approach: "Add ) only when close_count < open_count"

    - title: Modifying Strings In-Place Incorrectly
      description: |
        In languages with mutable strings or when using a list to build the string, forgetting to backtrack (remove the last character after recursion) leads to corrupted results.

        In Python, passing `current + '('` creates a new string, so backtracking is automatic. But if using a list like `current.append('(')`, you must call `current.pop()` after the recursive call returns.

  key_takeaways:
    - "**Backtracking pattern**: Build solutions incrementally, using constraints to prune invalid paths early"
    - "**Decision tree thinking**: Visualise recursive problems as trees where each node is a choice point"
    - "**Catalan numbers**: The count of valid parentheses combinations follows the Catalan sequence — this appears in many combinatorial problems"
    - "**Constraint propagation**: Encoding validity rules into the recursion conditions is more efficient than post-hoc filtering"

  time_complexity: "O(4^n / √n). This is the n<sup>th</sup> Catalan number, representing the count of valid combinations. Each valid string takes O(n) to construct."
  space_complexity: "O(n). The recursion depth is at most `2n` (the length of each string), and we store the current string being built."

solutions:
  - approach_name: Backtracking
    is_optimal: true
    code: |
      def generate_parenthesis(n: int) -> list[str]:
          result = []

          def backtrack(current: str, open_count: int, close_count: int):
              # Base case: we've placed all 2n parentheses
              if len(current) == 2 * n:
                  result.append(current)
                  return

              # Choice 1: Add opening parenthesis if we haven't used all n
              if open_count < n:
                  backtrack(current + '(', open_count + 1, close_count)

              # Choice 2: Add closing parenthesis if it won't make string invalid
              if close_count < open_count:
                  backtrack(current + ')', open_count, close_count + 1)

          backtrack('', 0, 0)
          return result
    explanation: |
      **Time Complexity:** O(4^n / √n) — The number of valid sequences is the n<sup>th</sup> Catalan number.

      **Space Complexity:** O(n) — Recursion stack depth plus the current string being built.

      We recursively build strings by making valid choices at each step. The constraints (`open_count < n` and `close_count < open_count`) ensure we never explore invalid paths, making this efficient despite the exponential output size.

  - approach_name: Iterative with Stack
    is_optimal: false
    code: |
      def generate_parenthesis(n: int) -> list[str]:
          result = []
          # Stack holds tuples of (current_string, open_count, close_count)
          stack = [('', 0, 0)]

          while stack:
              current, open_count, close_count = stack.pop()

              # Base case: complete string
              if len(current) == 2 * n:
                  result.append(current)
                  continue

              # Add closing parenthesis option first (will be processed second due to LIFO)
              if close_count < open_count:
                  stack.append((current + ')', open_count, close_count + 1))

              # Add opening parenthesis option
              if open_count < n:
                  stack.append((current + '(', open_count + 1, close_count))

          return result
    explanation: |
      **Time Complexity:** O(4^n / √n) — Same as recursive, we explore all valid paths.

      **Space Complexity:** O(4^n / √n) — The stack can hold many partial solutions simultaneously.

      This converts the recursion to an explicit stack, which can be useful in languages with limited recursion depth. The logic is identical — we just manage the call stack manually. Note that space complexity is worse because we store all pending states on the heap rather than using the call stack.

  - approach_name: Dynamic Programming
    is_optimal: false
    code: |
      def generate_parenthesis(n: int) -> list[str]:
          # dp[i] contains all valid strings with i pairs of parentheses
          dp = [[] for _ in range(n + 1)]
          dp[0] = ['']  # Base case: empty string for 0 pairs

          for i in range(1, n + 1):
              # Build strings for i pairs using smaller subproblems
              # Pattern: "(" + dp[j] + ")" + dp[i-1-j] for all valid j
              for j in range(i):
                  for left in dp[j]:
                      for right in dp[i - 1 - j]:
                          dp[i].append('(' + left + ')' + right)

          return dp[n]
    explanation: |
      **Time Complexity:** O(4^n / √n) — We generate all Catalan(n) strings.

      **Space Complexity:** O(4^n / √n) — We store all valid strings for all values up to n.

      This builds solutions bottom-up. For `i` pairs, we consider all ways to split: `j` pairs inside the first `()` and `i-1-j` pairs after it. While correct, this uses more memory than backtracking since it stores all intermediate results.
