代码实现
class Solution {
public:
vector<string> result;
string path;
void backtracking(int n, int left, int right) {
if (path.size() == n * 2) {
result.push_back(path);
return;
}
if (left < n) {
path.push_back('(');
backtracking(n, left + 1, right);
path.pop_back();
}
if (right < left) {
path.push_back(')');
backtracking(n, left, right + 1);
path.pop_back();
}
}
vector<string> generateParenthesis(int n) {
result.clear();
path.clear();
backtracking(n, 0, 0);
return result;
}
};
执行流程
示例输入
n = 3
回溯生成过程
生成过程(树形结构):
→ "(" → "(" → "(" (Left = 3, Right = 0) → ")" → "(()"
→ ")" → "(()()"
→ ")" → "(()())"
→ "(" → "(" → ")" (Left = 2, Right = 1) → ")" → "(())"
→ ")" → "(())()"
最终输出
["((()))", "(()())", "(())()", "()(())", "()()()"]
关键思路
- 回溯法的框架
- 当前字符串长度为
2 * n
时,说明生成了一个有效的组合,加入结果集。 - 递归过程中,控制左括号和右括号的数量,确保始终有足够的右括号与左括号匹配。
- 合法的括号组合规则
- 在生成括号时,左括号的数量不能超过
n
,右括号的数量不能超过左括号的数量。 - 使用两个变量
left
和 right
分别记录当前生成的左括号和右括号的数量。
- 回溯操作
- 每次递归时通过
push_back()
添加括号,并在递归后通过 pop_back()
撤销选择。
- 终止条件
- 如果当前字符串的长度已经是
2 * n
,说明已经完成了一个有效组合,将其加入 result
。
时间 & 空间复杂度
操作 | 时间复杂度 | 空间复杂度 |
---|
生成所有组合 | O(4n/√n) | O(n) |
- 时间复杂度: 生成括号组合的问题可以通过卡特兰数来表示,时间复杂度为 O(4n/\sqrt{n}),其中 n是括号对数。
- 空间复杂度: 由于使用了递归和保存路径,空间复杂度为 O(n)。
基础语法解析
1. path.push_back('(')
和 path.push_back(')')
- 选择左括号和右括号,每次递归时决定是否添加左括号或右括号。
path.push_back('(');
path.push_back(')');
2. backtracking(n, left + 1, right)
和 backtracking(n, left, right + 1)
- 递归调用,根据当前条件递归生成子问题。
left + 1
:增加左括号数量;right + 1
:增加右括号数量。
backtracking(n, left + 1, right);
backtracking(n, left, right + 1);
3. path.pop_back()
- 回溯操作,撤销最近一次的选择,即移除最后加入的括号。
path.pop_back();
优化 & 变种
变种 1:给定字符集生成有效括号
如果给定的字符集是 {
, }
, (
, )
,可以通过相同的回溯思想生成有效的括号组合。只需要替换括号的符号即可。
void generateParenthesis(int n) {
string path;
generateBrackets(n, 0, 0, path);
}
总结
C++ 语法 / 结构 | 作用 |
---|
path.push_back('(') | 选择左括号,递归 |
path.push_back(')') | 选择右括号,递归 |
path.pop_back() | 回溯,撤销当前选择 |
时间复杂度: O(4^n / √n) | 生成所有组合的时间复杂度 |
空间复杂度: O(n) | 存储当前路径的空间 |