给定一个数字 n
,代表括号对数,请你设计一个函数,生成所有可能的 有效 括号组合。
示例 1:
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
示例 2:
输入:n = 1
输出:["()"]
回溯(Backtracking)是一种 试探搜索 的方法,我们在递归过程中不断尝试构造合法的括号序列,并在不满足条件时回退。
i == 2n
(左右括号总数达到 2n
)时,说明形成了一个有效的括号序列,加入结果列表。n
时,可以添加 左括号 '('
。')'
。class Solution {
private int n;
private final List ans = new ArrayList<>();
private char[] path;
public List generateParenthesis(int n) {
this.n = n;
path = new char[n * 2];
dfs(0, 0);
return ans;
}
private void dfs(int i, int open) {
if (i == n * 2) {
ans.add(new String(path));
return;
}
if (open < n) { // 可以放左括号
path[i] = '(';
dfs(i + 1, open + 1);
}
if (i - open < open) { // 右括号数量必须小于左括号
path[i] = ')';
dfs(i + 1, open);
}
}
}
使用 StringBuilder
代替 char[]
StringBuilder
的可变特性可以减少字符串的创建开销,提高效率。
减少不必要的参数
open
变量可以用 left
和 right
分别表示 剩余可用的左括号 和 剩余可用的右括号,更直观。
class Solution {
private List ans = new ArrayList<>();
public List generateParenthesis(int n) {
backtrack(new StringBuilder(), n, n);
return ans;
}
private void backtrack(StringBuilder path, int left, int right) {
if (left == 0 && right == 0) { // 左右括号都用完了
ans.add(path.toString());
return;
}
if (left > 0) { // 还能放左括号
path.append('(');
backtrack(path, left - 1, right);
path.deleteCharAt(path.length() - 1); // 回溯
}
if (right > left) { // 右括号数量必须大于左括号
path.append(')');
backtrack(path, left, right - 1);
path.deleteCharAt(path.length() - 1); // 回溯
}
}
}
时间复杂度:O(4^n / sqrt(n))
由卡塔兰数 C_n = (1 / (n+1)) * C(2n, n)
推导得出。
空间复杂度:O(n)
(递归深度最多为 2n
)。
✅ 回溯法 通过递归搜索所有可能的括号组合,确保生成的括号是有效的。
✅ 通过 优化字符串操作 和 减少参数传递,可以提高效率。
✅ 理解 left
和 right
变量 是写出高效代码的关键。
如果觉得有帮助,记得点赞+收藏!你的支持是我持续更新的动力!