Given n
pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
A parentheses string is well-formed if and only if:
It is the empty string, or
It can be written as AB (A concatenated with B), where A and B are well-formed, or
It can be written as (A), where A is well-formed.
Example 1:
Input: n = 3 Output: ["((()))","(()())","(())()","()(())","()()()"]
Example 2:
Input: n = 1 Output: ["()"]
Note: 1 <= n <= 8
The core challenge of this problem is to generate all possible combinations of well-formed parentheses given n
pairs. This problem is significant in various applications such as validating expressions in compilers, generating code, and more. A common pitfall is to generate invalid combinations, which do not have matching opening and closing parentheses.
To solve this problem, we can use a backtracking approach. Backtracking is a general algorithm for finding all (or some) solutions to computational problems, notably constraint satisfaction problems. The idea is to build the solution incrementally and remove those solutions that fail to satisfy the constraints at any point in time.
A naive solution would be to generate all possible sequences of parentheses and then filter out the valid ones. However, this approach is not optimal because it generates a lot of invalid sequences, leading to unnecessary computations.
The optimized solution involves using backtracking to generate only valid sequences. We keep track of the number of opening and closing parentheses used so far and ensure that at any point, the number of closing parentheses does not exceed the number of opening ones.
Here is a step-by-step breakdown of the backtracking algorithm:
2 * n
, add it to the results list.n
, add an opening parenthesis and recurse./**
* Generate all combinations of well-formed parentheses.
* @param {number} n - Number of pairs of parentheses.
* @return {string[]} - List of all combinations of well-formed parentheses.
*/
function generateParenthesis(n) {
const result = [];
// Helper function for backtracking
function backtrack(current, open, close) {
// If the current string's length is 2 * n, it's a valid combination
if (current.length === 2 * n) {
result.push(current);
return;
}
// If we can add an opening parenthesis, do so and recurse
if (open < n) {
backtrack(current + '(', open + 1, close);
}
// If we can add a closing parenthesis, do so and recurse
if (close < open) {
backtrack(current + ')', open, close + 1);
}
}
// Start the backtracking process with an empty string
backtrack('', 0, 0);
return result;
}
// Example usage:
console.log(generateParenthesis(3)); // ["((()))","(()())","(())()","()(())","()()()"]
The time complexity of this approach is O(4^n / sqrt(n)), which is derived from the Catalan number. The space complexity is O(n) due to the recursion stack.
Potential edge cases include:
n = 0
: The output should be an empty list.n = 1
: The output should be ["()"].These cases are handled naturally by the algorithm.
To test the solution comprehensively, consider the following test cases:
n = 0
: Expected output is []n = 1
: Expected output is ["()"]n = 2
: Expected output is ["(())", "()()"]n = 3
: Expected output is ["((()))","(()())","(())()","()(())","()()()"]Use a testing framework like Jest or Mocha to automate these tests.
When approaching such problems, consider the following tips:
Practice similar problems to improve your problem-solving skills.
Generating well-formed parentheses is a classic problem that can be efficiently solved using backtracking. Understanding and implementing this solution helps in mastering recursion and backtracking techniques, which are essential for solving many complex problems.
For further reading and practice, consider the following resources: