Generate Parentheses
Table of Contents + β
This question looks like a string puzzle. But it is really about choices. At each step you decide what character to place next. Make the right choices and you only build valid answers. This is the classic way interviewers test if you understand backtracking.
π― The Problem
You get one number n and must build every valid way to arrange that many pairs of parentheses.
nis the number of pairs, so you havenopen(andnclose).- Valid means every
(has a matching). - Valid also means no
)ever comes before its(. - Return every valid combination, not just the count.
A quick read of n = 2. That means two ( and two ). The valid strings are (()) and ()(). A string like )( is not valid, because the close bracket comes first. A string like (() is not valid either, because one open bracket never closes.
Input: n = 2Output: ["(())", "()()"]
Explanation: both strings have 2 open and 2 close brackets, all matched.Here is the rule that keeps a string valid as we build it, drawn as a decision.
π’ Approach 1: Build All, Then Filter (Brute Force)
Make every string of ( and ), then keep the valid ones.
The idea:
- Generate every string of length
2nusing(and). - Check each one for matched brackets.
- Keep only the valid strings.
Why it is weak:
- For length
2nthere are2raised to the power2nstrings. - Most of them are junk that you build and throw away.
- For
n = 5that is over a thousand strings, and only a few are valid. - The wasted work makes it far too slow.
Here is the build-all-then-filter code:
def generate_parenthesis(n): def valid(text): balance = 0 for ch in text: balance += 1 if ch == "(" else -1 if balance < 0: return False return balance == 0
result = [] def build(text): if len(text) == 2 * n: if valid(text): result.append(text) return build(text + "(") build(text + ")")
build("") return resultβ‘ Approach 2: Backtracking (Best)
The idea in one line: build the string one character at a time, but only ever add a character that keeps it valid.
The idea:
- Backtracking means make a choice, go deeper, then undo the choice and try another path.
- Track two counts: how many
(used and how many)used.
How it works:
- You may add
(only while the open count is still less thann. - You may add
)only while the close count is still less than the open count. - That second rule stops you from closing a bracket that was never opened.
- When the string reaches length
2n, both counts hitn, so it is complete and valid. Save it.
Why it is fast:
- We never make an invalid move, so we never build a string we throw away.
- It only visits valid paths and a few dead ends.
- The count of valid strings is the nth Catalan number, which grows far slower than
2to the power2n.
Here is a small piece of the choice tree for n = 2. Each box shows the string so far.
Steps to Solve
- Keep the string so far, the open count, and the close count.
- If the string length equals
2n, save it and stop this path. - If the open count is less than
n, add(, go deeper, then remove it. - If the close count is less than the open count, add
), go deeper, then remove it. - Start the whole thing with an empty string and both counts at zero.
This Python version keeps the string so far and recurses, adding a bracket only when it stays valid.
def generate_parentheses(n): result = []
def backtrack(current, open_count, close_count): if len(current) == 2 * n: # full and valid string result.append(current) return if open_count < n: # may add an open bracket backtrack(current + "(", open_count + 1, close_count) if close_count < open_count: # may add a close bracket backtrack(current + ")", open_count, close_count + 1)
backtrack("", 0, 0) return result
print(generate_parentheses(2))The output of the above code will be:
['(())', '()()']Let us read the Python version line by line and see the reason behind each piece.
The line result = [] holds the finished strings. We collect them here. The inner function backtrack does the real work. It takes three things: the string built so far, the open count, and the close count.
The first check is if len(current) == 2 * n. A full valid string has n opens and n closes, so its length is 2n. When we reach that length, the string is done. We add it to result and return to stop this path.
The next check is if open_count < n. This is the rule for adding an open bracket. We may add ( only while we have not used all n opens. We call backtrack(current + "(", open_count + 1, close_count). Notice we pass current + "(", a brand new string. Because strings in Python do not change in place, the old current stays safe for the next branch. So we do not need to manually undo anything. That is why this version has no explicit delete step, unlike the Java one.
The last check is if close_count < open_count. This is the heart of the trick. We may add ) only when there are more opens than closes. That guarantees every close has a waiting open. We call backtrack again with the close count raised. Finally backtrack("", 0, 0) starts everything from an empty string with both counts at zero.
β±οΈ Time and Space Complexity
The count of valid strings for n pairs is the nth Catalan number. A Catalan number is a special counting number that grows fast but far slower than the full 2 to the power 2n. So backtracking only touches valid paths and a few dead ends. The brute force builds all 2 to the power 2n strings, which is much more work. Both store the output, so the space is tied to the number of valid strings times their length.
| Approach | Time Complexity | Space Complexity |
|---|---|---|
| Build all, then filter | O(2^(2n) * n) | O(n) |
| Backtracking | O(4^n / sqrt(n)) | O(n) |
Tip
The two rules are the whole answer. Add an open only if open is less than n. Add a close only if close is less than open. Memorize those two lines and this problem becomes easy.
π§© Key Takeaways
- β Build only valid strings instead of building everything and filtering.
- β Track the open count and the close count as you go.
- β
Add
(only while open is less thann. Add)only while close is less than open. - β
A string is complete when its length reaches
2n. - β Backtracking explores far fewer strings than brute force, so it is much faster.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What does Generate Parentheses ask you to produce?
Why: You must build and return every valid combination of n pairs of parentheses.
- 2
When may you safely add a close bracket ')' during backtracking?
Why: Adding ')' only when close is less than open guarantees every close has a matching open.
- 3
Why is brute force slow for this problem?
Why: Brute force makes every possible string of length 2n, but only a small share are valid.
- 4
How do we know a built string is complete and valid?
Why: By following the two rules, a string of length 2n always has n matched pairs, so it is complete and valid.