The problem of Generating Parentheses is to find all combinations of well-formed parentheses given n
pairs of parentheses. A well-formed parentheses sequence is a string consisting of n
pairs of parentheses where each opening parenthesis (
is closed by a corresponding closing parenthesis )
.
Problem Statement:
Given n
, generate all combinations of well-formed parentheses that can be made using n
pairs of parentheses.
Example:
Input:
n = 3
Output:
[ "((()))", "(()())", "(())()", "()()()", "()(()))"]
Explanation: For n = 3
, there are 5 possible combinations of valid parentheses.
Approach:
To generate all valid parentheses combinations, we can use Backtracking. The idea is to keep track of the number of opening (
and closing )
parentheses used so far. We can add an opening parenthesis if we have not yet used n
opening parentheses, and we can add a closing parenthesis if the number of closing parentheses used is less than the number of opening parentheses used.
- Base Case: When both the number of opening and closing parentheses used is equal to
n
, we have a valid combination of parentheses. - Recursive Case:
- If we have not yet used
n
opening parentheses, we can add an opening parenthesis. - If the number of closing parentheses used is less than the number of opening parentheses, we can add a closing parenthesis.
- If we have not yet used
- Backtracking: The algorithm explores all possible valid configurations and backtracks when necessary.
Time Complexity:
- Time complexity: The number of valid combinations is Catalan number
C(n)
. The total number of operations is proportional toC(n)
. Each valid sequence takesO(n)
time to generate, so the total time complexity is O(4^n / √n). - Space complexity: We need space for the recursion stack and storing the valid parentheses sequences. The space complexity is O(n) for the recursion and O(C(n)) for storing the results.
Algorithm:
- Initialize an empty list to store the results.
- Define a recursive function that takes the current string of parentheses, the number of opening parentheses used, and the number of closing parentheses used.
- Recursively build valid parentheses strings by adding
(
or)
based on the current state. - When a valid string is found (i.e., both opening and closing parentheses used equals
n
), add it to the result list. - Return the result list.
Code Implementation in Different Languages:
1. C
#include <stdio.h>
#include <string.h>
void generateParentheses(int n, int open, int close, char *current, char **result, int *resultIndex) {
if (open == n && close == n) {
result[*resultIndex] = strdup(current);
(*resultIndex)++;
return;
}
if (open < n) {
current[strlen(current)] = '(';
generateParentheses(n, open + 1, close, current, result, resultIndex);
current[strlen(current) - 1] = '\0';
}
if (close < open) {
current[strlen(current)] = ')';
generateParentheses(n, open, close + 1, current, result, resultIndex);
current[strlen(current) - 1] = '\0';
}
}
char** generateParenthesis(int n, int *returnSize) {
char **result = (char **)malloc(sizeof(char*) * 1000);
*returnSize = 0;
char current[2 * n + 1];
current[0] = '\0';
generateParentheses(n, 0, 0, current, result, returnSize);
return result;
}
int main() {
int n = 3;
int returnSize = 0;
char **result = generateParenthesis(n, &returnSize);
for (int i = 0; i < returnSize; i++) {
printf("%s\n", result[i]);
}
return 0;
}
2. C++
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void generateParentheses(int n, int open, int close, string ¤t, vector<string> &result) {
if (open == n && close == n) {
result.push_back(current);
return;
}
if (open < n) {
current.push_back('(');
generateParentheses(n, open + 1, close, current, result);
current.pop_back();
}
if (close < open) {
current.push_back(')');
generateParentheses(n, open, close + 1, current, result);
current.pop_back();
}
}
vector<string> generateParenthesis(int n) {
vector<string> result;
string current;
generateParentheses(n, 0, 0, current, result);
return result;
}
int main() {
int n = 3;
vector<string> result = generateParenthesis(n);
for (const string &str : result) {
cout << str << endl;
}
return 0;
}
3. Java
import java.util.ArrayList;
import java.util.List;
public class Solution {
public void generateParentheses(int n, int open, int close, String current, List<String> result) {
if (open == n && close == n) {
result.add(current);
return;
}
if (open < n) {
generateParentheses(n, open + 1, close, current + "(", result);
}
if (close < open) {
generateParentheses(n, open, close + 1, current + ")", result);
}
}
public List<String> generateParenthesis(int n) {
List<String> result = new ArrayList<>();
generateParentheses(n, 0, 0, "", result);
return result;
}
public static void main(String[] args) {
Solution solution = new Solution();
List<String> result = solution.generateParenthesis(3);
for (String s : result) {
System.out.println(s);
}
}
}
4. Python
def generateParentheses(n: int):
result = []
def backtrack(current, open, close):
if open == n and close == n:
result.append(current)
return
if open < n:
backtrack(current + '(', open + 1, close)
if close < open:
backtrack(current + ')', open, close + 1)
backtrack("", 0, 0)
return result
# Test the function
print(generateParentheses(3))
5. C#
using System;
using System.Collections.Generic;
public class Solution {
public void GenerateParentheses(int n, int open, int close, string current, List<string> result) {
if (open == n && close == n) {
result.Add(current);
return;
}
if (open < n) {
GenerateParentheses(n, open + 1, close, current + "(", result);
}
if (close < open) {
GenerateParentheses(n, open, close + 1, current + ")", result);
}
}
public List<string> GenerateParenthesis(int n) {
List<string> result = new List<string>();
GenerateParentheses(n, 0, 0, "", result);
return result;
}
public static void Main() {
Solution solution = new Solution();
List<string> result = solution.GenerateParenthesis(3);
foreach (string s in result) {
Console.WriteLine(s);
}
}
}
6. JavaScript
var generateParenthesis = function(n) {
const result = [];
function backtrack(current, open, close) {
if (open === n && close === n) {
result.push(current);
return;
}
if (open < n) {
backtrack(current + '(', open + 1, close);
}
if (close < open) {
backtrack(current + ')', open, close + 1);
}
}
backtrack("", 0, 0);
return result;
};
// Test the function
console.log(generateParenthesis(3));
Conclusion:
- Backtracking is the key technique for solving the “Generate Parentheses” problem.
- By maintaining counts of the open and close parentheses, we can ensure that the generated strings are valid without needing to check them post-generation.
- The time complexity is O(4^n / √n) due to the number of valid parentheses sequences.