class Solution {
public:
vector<string> ans; // 保存所有合法的括号组合
string t; // 当前正在构造的括号字符串
void dfs(int l, int r, int n) {
// 左右括号都已经使用 n 个,得到一个合法答案
if (l == n && r == n) {
ans.push_back(t);
return;
}
// 1. 放左括号:左括号还没有使用完
if (l < n) {
t.push_back('('); // 做选择
dfs(l + 1, r, n); // 左括号使用数量 +1
t.pop_back(); // 回溯,撤销选择
}
// 2. 放右括号:右括号数量必须少于左括号数量
if (r < l) {
t.push_back(')'); // 做选择
dfs(l, r + 1, n); // 右括号使用数量 +1
t.pop_back(); // 回溯,撤销选择
}
}
vector<string> generateParenthesis(int n) {
// 初始时左右括号都还没有使用
dfs(0, 0, n);
return ans;
}
};
1. ans
vector<string> ans;
用于保存最终所有合法括号组合。
例如:
n = 3
最后:
ans = [
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
2. t
string t;
表示:
当前 DFS 路径正在构造的括号字符串。
例如搜索过程中可能依次变成:
""
"("
"(("
"(()"
"(())"
每次:
t.push_back('(');
或者:
t.push_back(')');
都是在当前路径上添加一个括号。
而:
t.pop_back();
就是回溯,删除刚刚加入的括号。
3. l
int l
表示:
已经使用了多少个左括号
(。
例如:
t = "(()"
里面有两个左括号:
l = 2
注意这里的 l 不是"还剩多少个左括号",而是:
已经用了多少个。
4. r
int r
表示:
已经使用了多少个右括号
)。
比如:
t = "(()"
里面有一个右括号:
r = 1
所以此时:
l = 2
r = 1
5. n
int n
表示总共需要使用:
n 个左括号
n 个右括号
例如:
n = 3
最终字符串长度一定是:
2 × n = 6
dfs() 函数是干什么的
void dfs(int l, int r, int n)
可以直接理解成:
当前已经使用了
l个左括号和r个右括号,继续往后构造合法的括号字符串。
所以:
dfs(0, 0, n);
表示:
现在一个括号都没放,从头开始搜索。
第一个终止条件
if (l == n && r == n) {
ans.push_back(t);
return;
}
如果:
l == n
r == n
说明:
n 个左括号全部用了
n 个右括号全部用了
那么当前字符串已经构造完成。
例如:
n = 3
t = "(()())"
l = 3
r = 3
所以把:
ans.push_back(t);
保存下来。
为什么 l < n 才能放左括号
if (l < n)
因为总共只能使用 n 个左括号。
比如:
n = 3
l = 2
说明目前用了两个:
((
还可以再放一个。
但是如果:
l = 3
已经用满了,就不能继续:
t.push_back('(');
否则会超过题目要求。
所以:
if (l < n)
意思就是:
左括号还有剩余,可以继续放。
为什么放左括号以后是 l + 1
t.push_back('(');
dfs(l + 1, r, n);
因为刚才使用了一个左括号。
例如原来:
l = 1
r = 0
t = "("
再加入:
'('
变成:
t = "(("
l = 2
r = 0
所以递归:
dfs(l + 1, r, n);
为什么右括号必须满足 r < l
这是这道题最重要的条件:
if (r < l)
表示:
只有当前已经使用的右括号数量比左括号少,才能继续放右括号。
例如:
t = "(("
l = 2
r = 0
可以放:
)
得到:
"(()"
没有问题。
但是如果当前:
t = "()"
l = 1
r = 1
此时如果继续放右括号:
"())"
就非法了。
所以当:
r == l
的时候不能再放右括号。
必须:
r < l
才能放。
整个 DFS 在做什么
每一层其实就是决定:
下一个位置放什么?
有两个可能:
当前状态
/ \
放左括号 放右括号
l < n r < l
所以代码就是:
// 可以放左括号
if (l < n) {
t.push_back('(');
dfs(l + 1, r, n);
t.pop_back();
}
// 可以放右括号
if (r < l) {
t.push_back(')');
dfs(l, r + 1, n);
t.pop_back();
}
最后记住这几个变量
ans → 保存最终答案
t → 当前正在构造的字符串
l → 已经用了多少个左括号
r → 已经用了多少个右括号
n → 左右括号分别需要使用多少个
整道题一句话总结:
DFS 每一层决定当前位置放左括号还是右括号;左括号只要
l < n就可以放,右括号只有r < l时才能放,从而保证括号始终合法;递归结束后用pop_back()回溯恢复现场。