题目
标题和出处
标题:括号生成
出处:22. 括号生成
难度
5 级
题目描述
要求
给定数字 n \texttt{n} n 代表生成括号的对数,设计一个函数,用于生成所有有效的括号组合。
示例
示例 1:
输入:
n
=
3
\texttt{n = 3}
n = 3
输出:
["((()))","(()())","(())()","()(())","()()()"]
\texttt{["((()))","(()())","(())()","()(())","()()()"]}
["((()))","(()())","(())()","()(())","()()()"]
示例 2:
输入:
n
=
1
\texttt{n = 1}
n = 1
输出:
["()"]
\texttt{["()"]}
["()"]
数据范围
- 1 ≤ n ≤ 8 \texttt{1} \le \texttt{n} \le \texttt{8} 1≤n≤8
解法一
思路和算法
由于每一对括号由一个左括号和一个右括号组成,因此生成 n n n 对括号会产生长度为 2 n 2n 2n 的字符串。有效的 n n n 对括号组合满足左括号和右括号各有 n n n 个,且每对括号中的左括号都在右括号前面。
最直观的做法是对于每个字符分别尝试左括号和右括号,然后判断每个字符串是否为有效的括号组合。每个字符都有 2 2 2 种可能,所有可能的字符串数是 2 2 n = 4 n 2^{2n} = 4^n 22n=4n,大多数字符串都不是有效的括号组合,因此该做法的时间复杂度较高。如果可以排除不符合要求的字符串,则可以降低时间复杂度。
可以使用回溯得到所有有效的括号组合。回溯的做法是使用一个字符串存储当前括号组合,维护已经添加的左括号和右括号个数,每次尝试分别将左括号和右括号添加到字符串的末尾,确保任何时刻字符串都符合有效括号组合的规则。具体做法如下。
-
如果字符串的长度等于 2 n 2n 2n,则得到一个有效的括号组合,将字符串添加到答案中。
-
如果字符串的长度小于 2 n 2n 2n,则需要将一个括号添加到字符串的末尾,然后继续回溯,需要分别判断左括号和右括号是否可以添加。
-
如果左括号个数小于 n n n,则可以添加左括号。将左括号添加到字符串的末尾之后,将左括号个数加 1 1 1,继续回溯。
-
如果右括号个数小于左括号个数,则可以添加右括号。将右括号添加到字符串的末尾之后,将右括号个数加 1 1 1,继续回溯。
-
回溯结束时,即可得到所有有效的括号组合。
代码
class Solution {
List<String> parentheses = new ArrayList<String>();
StringBuffer temp = new StringBuffer();
int n;
public List<String> generateParenthesis(int n) {
this.n = n;
backtrack(0, 0);
return parentheses;
}
public void backtrack(int left, int right) {
if (temp.length() == 2 * n) {
parentheses.add(temp.toString());
} else {
if (left < n) {
temp.append('(');
backtrack(left + 1, right);
temp.deleteCharAt(temp.length() - 1);
}
if (right < left) {
temp.append(')');
backtrack(left, right + 1);
temp.deleteCharAt(temp.length() - 1);
}
}
}
}
复杂度分析
-
时间复杂度: O ( 4 n n ) O(\dfrac{4^n}{\sqrt{n}}) O(n4n),其中 n n n 是生成括号的对数。有效的 n n n 对括号组合的个数是第 n n n 个卡特兰数 C 2 n n n + 1 \dfrac{C_{2n}^n}{n + 1} n+1C2nn,其渐进上界为 O ( 4 n n n ) O(\dfrac{4^n}{n \sqrt{n}}) O(nn4n),对于每个有效的括号组合需要 O ( n ) O(n) O(n) 的时间添加到答案中,因此时间复杂度是 O ( 4 n n ) O(\dfrac{4^n}{\sqrt{n}}) O(n4n)。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是生成括号的对数。存储括号组合的字符串和递归调用栈需要 O ( n ) O(n) O(n) 的空间。注意返回值不计入空间复杂度。
解法二
思路和算法
任何有效的括号组合的首个字符一定是左括号,且首个左括号一定有一个对应的右括号,因此有效的 n n n 对括号组合可以表示成 ( left ) right (\textit{left})\textit{right} (left)right 的形式,其中 left \textit{left} left 和 right \textit{right} right 分别是有效的 i i i 对括号组合和有效的 j j j 对括号组合,其中 0 ≤ i , j < n 0 \le i, j < n 0≤i,j<n 且 i + j = n − 1 i + j = n - 1 i+j=n−1。规定有效的 0 0 0 对括号组合是空字符串。
因此可以按照括号对数递归生成有效的括号组合,递归的做法如下。
-
当 n = 0 n = 0 n=0 时,为递归的终止条件,有效的括号组合只有空字符串。
-
当 n > 0 n > 0 n>0 时,对于所有满足 0 ≤ i , j < n 0 \le i, j < n 0≤i,j<n 且 i + j = n − 1 i + j = n - 1 i+j=n−1 的整数 i i i 和 j j j,分别得到所有有效的 i i i 对括号组合和所有有效的 j j j 对括号组合,用 left \textit{left} left 和 right \textit{right} right 分别表示有效的 i i i 对括号组合和有效的 j j j 对括号组合,枚举每个 left \textit{left} left 和 right \textit{right} right,则 ( left ) right (\textit{left})\textit{right} (left)right 是有效的 n n n 对括号组合,将其添加到答案中。
递归结束时,即可得到所有有效的括号组合。
代码
class Solution {
public List<String> generateParenthesis(int n) {
List<String> parentheses = new ArrayList<String>();
if (n == 0) {
parentheses.add("");
} else {
for (int i = 0, j = n - 1; i < n; i++, j--) {
List<String> leftList = generateParenthesis(i);
List<String> rightList = generateParenthesis(j);
for (String left : leftList) {
for (String right : rightList) {
parentheses.add("(" + left + ")" + right);
}
}
}
}
return parentheses;
}
}
复杂度分析
-
时间复杂度: O ( 4 n n ) O(\dfrac{4^n}{\sqrt{n}}) O(n4n),其中 n n n 是生成括号的对数。有效的 n n n 对括号组合的个数是第 n n n 个卡特兰数 C 2 n n n + 1 \dfrac{C_{2n}^n}{n + 1} n+1C2nn,其渐进上界为 O ( 4 n n n ) O(\dfrac{4^n}{n \sqrt{n}}) O(nn4n),对于每个有效的括号组合需要 O ( n ) O(n) O(n) 的时间添加到答案中,因此时间复杂度是 O ( 4 n n ) O(\dfrac{4^n}{\sqrt{n}}) O(n4n)。
-
空间复杂度: O ( 4 n n ) O(\dfrac{4^n}{\sqrt{n}}) O(n4n),其中 n n n 是生成括号的对数。递归调用栈需要 O ( n ) O(n) O(n) 的空间,中间结果需要 O ( 4 n n ) O(\dfrac{4^n}{\sqrt{n}}) O(n4n) 的空间。


被折叠的 条评论
为什么被折叠?



