Leetcode 22 - Generate Parentheses
Understanding the Problem
The goal is to generate all combinations of well-formed parentheses given \(n\) pairs. A combination is well-formed if every opening parenthesis ( has a matching closing parenthesis ) and no closing parenthesis appears without a preceding unmatched opening parenthesis.
To solve this efficiently, we use a Backtracking (DFS) approach to build the combinations incrementally branch by branch:
- We keep track of the remaining count of open brackets (
open_count) and closing brackets (close_count) available to place. - Placing
(: We can always place an opening parenthesis as long as we have pairs left to open (open_count > 0). - Placing
): We can only place a closing parenthesis if it doesn't violate well-formedness. This means we must have more closing brackets remaining than opening brackets (close_count > open_count), proving that an open bracket is currently waiting to be matched. - Base Case: When both
open_countandclose_countreach0, a valid combination of length \(2n\) is complete and added to our results.
💡 Avoid Mutating State Variables: A common pitfall in Python backtracking is using
list.append()inside recursive arguments (e.g.,curr_str.append('(')). Becauseappend()mutates lists in-place and returnsNone, it breaks the recursive chain. Utilizing immutable string concatenation (curr_str + '(') cleanly isolates state choices to their respective recursion paths.
Solution Implementation
Code
class Solution:
def generateParenthesis(self, n: int) -> list[str]:
result = []
def dfs(open_count, close_count, curr_str):
# Base case: if no brackets are left to place, combination is valid
if open_count == 0 and close_count == 0:
result.append(curr_str)
return
# If we have open brackets left, we can always add one
if open_count > 0:
dfs(open_count - 1, close_count, curr_str + '(')
# We can only add a close bracket if it matches a previously placed open one
if close_count > open_count:
dfs(open_count, close_count - 1, curr_str + ')')
# Start the recursion tree with 'n' open and 'n' close brackets available
dfs(n, n, "")
return result
### Time Complexity
The number of valid parenthesis combinations generated is exactly equal to the $n$-th Catalan number:
$$C_n = \frac{1}{n+1}\binom{2n}{n}$$
The asymptotic growth of the Catalan number is bounded by $\frac{4^n}{n\sqrt{n}}$. Since we spend $O(n)$ time to construct and copy each valid string of length $2n$ into our final results array:
**Overall time complexity — $O\left(\frac{4^n}{\sqrt{n}}\right)$**
### Space Complexity
- **Recursion Stack:** The maximum depth of the runtime call stack corresponds to the maximum length of a combination string, which is $2n$ recursive frames.
- **State Tracking:** Each recursive frame stores a string configuration of at most length $2n$. Excluding the memory required to hold the final output list, the space is dominated by the depth of this recursion tree.
**Overall space complexity — $O(n)$**
---
## Additional Resources
- https://neetcode.io/problems/generate-parentheses
- https://leetcode.com/problems/generate-parentheses/