Generate All Valid Parentheses
- mosesg1123
- Mar 18
- 4 min read
Updated: Apr 22
Problem Statement
Given an integer n, write a function to generate all possible combinations of n pairs of valid parentheses.
Example 1
Input: n = 3
Output:
["((()))", "(()())", "(())()", "()(())", "()()()"]Example 2
Input: n = 2
Output:
["(())", "()()"]1. Clarify Requirements Before Jumping Into Code
Before writing a single line of code, let's clarify a few things:
What constitutes "valid" parentheses?
I confirmed that each opening parenthesis must have a matching closing one, and at no point in the sequence should a closing parenthesis appear before its corresponding opening.
Constraints on n:
I asked if n can be zero or if there's a maximum value I need to worry about. Typically, n is a non-negative integer. For the sake of this question, let's assume the interviewer gave a maximum value for n of 10.
It may seem trivial to clarify these points, but it's important to make this sort of clarification a habit, even with simple questions. More complex questions will be that much harder for you if you jump into solutions with faulty assumptions.
2. Identify the Category of the Question
This one is easy - the problem falls under the recursion and backtracking category. It’s a combinatorial problem where I need to generate all possible valid sequences, which naturally lends itself to recursive solutions.
3. Consider a Brute-Force Approach to Understand the Problem
The brute-force method would look something like this:
Generate all possible sequences of 2n characters (each character being either '(' or ')').
Filter out invalid sequences by checking each one for balance.
This approach would generate 2^(2n) sequences, or 4^n
Each sequence takes O(n) time to validate.
Therefore, the total time complexity is O((4^n) * n)
Why is this not a great solution? It wastes time and resources on sequences that can be identified as invalid early on.
4. Brainstorm More Solutions
Let's brainstorm some ideas to be more efficient. We still need to use recursion, but:
I can keep track of how many opening and closing parentheses I've added.
At each step, I decide whether to add an opening parenthesis (if I haven’t reached n yet) or a closing one (if it won’t lead to an invalid sequence).
This strategy avoids generating invalid sequences entirely.
At each step, we either add an opening parenthesis '(' (if we haven't used all n) or a closing parenthesis ')' (if it remains valid to do so).
This decision tree leads to roughly 2ⁿ possible function calls in the worst case.
This backtracking approach is much more efficient because it prunes the search space early by only pursuing valid partial sequences.
5. Discuss Trade-Offs Between Your Solutions
Trade-Off Discussion:
Brute-Force Approach:
Pros: Conceptually simple.
Cons: Exponential time complexity and inefficient, as it generates many invalid sequences.
Recursive Backtracking:
Pros: Efficiently generates only valid combinations, with a significant reduction in unnecessary work.
Cons: Requires careful management of recursion and understanding of base/recursive cases.
The backtracking approach is more elegant and practical, especially when n grows larger. It leverages the inherent structure of the problem to avoid redundant computations.
6. Write Pseudocode to Structure Your Thoughts
Pseudocode Outline:
function generateParentheses(n):
result = []
function backtrack(current_string, open, close):
if length(current_string) == 2 * n:
add current_string to result
return
if open < n:
backtrack(current_string + '(', open + 1, close)
if close < open:
backtrack(current_string + ')', open, close + 1)
backtrack("", 0, 0)
return result
Explanation:
I start with an empty string and zero counts for both open and close parentheses.
I recursively add an opening parenthesis if I haven't added n yet.
I add a closing parenthesis only if it won’t break the validity (i.e., if there are more opens than closes).
7. Consider Edge Cases
Edge Cases I Addressed:
n = 0: The output should simply be an empty list containing an empty string [“”].
n = 1: The result should be straightforward: ["()"].
Input Validation: Optionally, I could check if n is negative and handle it gracefully (e.g., by returning an empty list or raising an error).
8. Write Full Code Syntax
Here’s the solution using Python:
def generate_parentheses(n):
result = []
def backtrack(current_string, open_count, close_count):
# Base case: if the current string is complete, add it to results.
if len(current_string) == 2 * n:
result.append(current_string)
return
# If we can add an opening parenthesis, do it.
if open_count < n:
backtrack(current_string + '(', open_count + 1, close_count)
# If we can add a closing parenthesis without breaking the validity, do it.
if close_count < open_count:
backtrack(current_string + ')', open_count, close_count + 1)
backtrack("", 0, 0)
return result
# Example usage:
print(generate_parentheses(3))
9. Test Your Code
Testing Strategy:I made sure to test with various values of n:
n = 0: Expected output: [""]
n = 1: Expected output: ["()"]
n = 3: Expected output: A list containing ["((()))", "(()())", "(())()", "()(())", "()()()"]
Conclusion
By clarifying the requirements, categorizing the problem, considering a brute-force approach, brainstorming more efficient solutions, discussing trade-offs, writing pseudocode, considering edge cases, coding, and finally testing my solution, I was able to confidently tackle the "Generate All Valid Parentheses" problem. This step-by-step method not only helps me solve the problem efficiently but also demonstrates my systematic approach to problem-solving during technical interviews.
Happy coding, and good luck on your next interview challenge!


Comments