Lesson 144 of 164
Generate Parentheses
Given n, generate every string of n pairs of parentheses that is balanced (well-formed).
LeetCode: 22. Generate Parentheses
Example
n = 3 -> ["((()))", "(()())", "(())()", "()(())", "()()()"]
n = 1 -> ["()"]Backtracking builds a solution one choice at a time. After exploring everything that follows a choice, it undoes the choice (backtracks) and tries the next option. Every problem in this section follows that pattern.
Brute force: generate everything, keep the valid ones
Generate all 2^(2n) strings of length 2n made of ( and ), and keep the balanced ones. A string is balanced if a running count (+1 for (, -1 for )) never goes negative and ends at 0.
import java.util.ArrayList;
import java.util.List;
public class Main {
static boolean isBalanced(CharSequence s) {
int depth = 0;
for (int i = 0; i < s.length(); i++) {
depth += s.charAt(i) == '(' ? 1 : -1;
if (depth < 0) return false;
}
return depth == 0;
}
static void generateAll(StringBuilder current, int len, List<String> out) {
if (current.length() == len) {
if (isBalanced(current)) out.add(current.toString());
return;
}
for (char c : new char[]{'(', ')'}) {
current.append(c);
generateAll(current, len, out);
current.deleteCharAt(current.length() - 1);
}
}
static List<String> generateParentheses(int n) {
List<String> out = new ArrayList<>();
generateAll(new StringBuilder(), 2 * n, out);
return out;
}
public static void main(String[] args) {
System.out.println(generateParentheses(3));
}
}[((())), (()()), (())(), ()(()), ()()()]StringBuilder is a mutable string buffer. append adds a character and deleteCharAt removes one, so the same buffer is reused and toString() copies it only for a finished answer.
Time: O(2^(2n) · n). For n = 3, 64 strings are built for 5 answers. Space: O(n) besides the output.
Optimal: only build valid prefixes
A prefix can always be extended to a balanced string as long as:
- it has at most
nopening parentheses, and - it never has more
)than(.
So add ( only while open < n, and add ) only while close < open. Every complete string built this way is balanced, and no work is wasted on dead ends.
graph TB
a["("] --> b["(("]
a --> c["()"]
b --> d["(()"]
b --> e["((("]
c --> f["()("]import java.util.ArrayList;
import java.util.List;
public class Main {
static void backtrack(StringBuilder current, int open, int close, int n, List<String> out) {
if (current.length() == 2 * n) {
out.add(current.toString());
return;
}
if (open < n) {
current.append('(');
backtrack(current, open + 1, close, n, out);
current.deleteCharAt(current.length() - 1);
}
if (close < open) {
current.append(')');
backtrack(current, open, close + 1, n, out);
current.deleteCharAt(current.length() - 1);
}
}
static List<String> generateParentheses(int n) {
List<String> out = new ArrayList<>();
backtrack(new StringBuilder(2 * n), 0, 0, n, out);
return out;
}
public static void main(String[] args) {
System.out.println(generateParentheses(3));
System.out.println(generateParentheses(1) + " " + generateParentheses(10).size());
List<String> none = generateParentheses(0);
System.out.println(none.size() + " " + none.get(0).isEmpty());
}
}[((())), (()()), (())(), ()(()), ()()()]
[()] 16796
1 trueList.toString prints strings without quotes, so () in the output is the string "()".
Time: O(Cₙ · n), where Cₙ is the nth Catalan number (16796 for n = 10), the number of balanced strings. Each is copied in O(n). Space: O(n) for the recursion and the current string, besides the output.
Edge cases
n = 0: one answer, the empty string. The last line of the program prints the list size and whether that answer is empty.- Order: trying
(before)produces the strings in lexicographic order, because'(' < ')'.
