top of page

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!

Recent Posts

See All
Non-Overlapping Intervals

Learn how to solve the Non-Overlapping Intervals coding problem to prepare for your next technical interview!

 
 
 
Merge Intervals

"Merge Intervals" is one of those classic algorithm problems that shows up frequently in technical interviews. It's a great test of your...

 
 
 
Jump Game II

Jump Game II is a classic follow-up to the original Jump Game problem. It’s not just about whether you can reach the end... now you have to do it in the fewest jumps possible! That small change turns

 
 
 

Comments


Drop Me a Line, Let Me Know What You Think

Thanks for submitting!

© 2023 by Train of Thoughts. Proudly created with Wix.com

bottom of page