题目:
Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
Example 1:
Input: n = 3
Output: ["((()))","(()())","(())()","()(())","()()()"]
方法1: brute force
假设我现在有个空篮子可以用来装括号,我先不管这个篮子的括号是否有效,每次我选开括号"(“或者闭括号”)"。选完后再判断篮子的括号是否有效。一共有 2 2 n 2^{2n} 22n个字符串。
时间复杂度
O
(
2
2
n
∗
n
)
=
O
(
4
n
n
)
O(2^{2n} * n) = O(4^nn)
O(22n∗n)=O(4nn)
空间复杂度
4
n
n
4^nn
4nn
方法2: 回溯 + 剪枝
回溯的概念举个例子,我面前有好几条路,每条路走完需要体力 t 0 t_0 t0。每次走完一条路,我都要把体力调整回 t 0 t_0 t0才能走完其他路。
剪枝就是很多路我不用走,或者走到一半就知道此路不通,可以回原地。
方法1是通过拿到所有的括号后判断是否有效,优化的策略是能不能之前排出那些绝对无效的组合,或者直走那些可能有效的路。
经过观察我们发现,
- 只要开括号的数量 < n, 我们可以一直加开括号
- 闭括号的数量 < 开括号的数量时,我们才能加闭括号
class Solution {
public List<String> generateParenthesis(int n) {
List<String> ans = new ArrayList<>();
dfs(n, 0, 0, new StringBuilder(), ans);
return ans;
}
public void backtrack(int n, int open, int close, StringBuilder comb, List<String> combs){
if(open == n && close == n){
combs.add(comb.toString());
return;
}
if(open < n){
comb.append("(");
backtrack(n, open + 1, close, comb, combs);
comb.deleteCharAt(comb.length() - 1);
}
if(open > close){
comb.append(")");
backtrack(n, open, close + 1, comb, combs);
comb.deleteCharAt(comb.length() - 1);
}
}
}
方法3: 动态规划dp
一个有效的括号字符串从开括号"(“开始,对应的闭括号”)“在字符串中的某处,可以写成”(left)right"。left, right也都是有效的括号字符串。假设n = 3, 此时已经有一组括号了,剩下的2组括号由left和right共同决定,如果left提供0组有效括号字符串,那么right提供2组。如果left提供1组有效括号字符串,那么right提供1组。因此,如果left提供c组,那么right提供n - 1 - c组, 同时我们要遍历n - 1,得出left能提供的所有组数。
一个大问题分解成了一系列子问题。这就是dp的思路。
下面是伪代码
def func(n):
if n = 0
return [""]
ans = []
for c in range(n)
for left in func(c)
for right in func(n - 1 - c)
ans.add("({left}){right}")
return ans
Java
class Solution {
public List<String> generateParenthesis(int n) {
List<String> ans = new ArrayList<>();
if (n == 0){
ans.add("");
return ans;
}
for(int i = 0; i < n; i++){
for(String left: generateParenthesis(i)){
for(String right: generateParenthesis(n - 1 - i)){
ans.add(String.format("(%s)%s", left, right));
}
}
}
return ans;
}
}