解决一个问题有好多步,每一步都有多个选项时,用回溯算法,回溯算法是一种树上的dfs,这里的树指的是递归树。回溯大致可分为3类:1.组合、子集 (无序)2.排列(有序)3.搜索
例如本题,我们分2n步,每一步有两个选项:左括号or右括号,所以使用回溯算法,本题属于组合问题。
按照回溯算法六步骤:
1.画递归树,找状态变量
2.判断结束条件
3.找出选择列表
4.判断枝剪
5.递归进入下一层
6.撤销
再回到本问题,首先显而易见结束条件就是凑够了n对括号,即2n个括号,可以判断字符串长度==2n来判断是否结束。选择列表每次都能选一个左括号或者右括号,但是(重点来了)!!!
1.左括号的数量不能大于n,否则没有足够的右括号与之闭合
2.每一步的右括号数量不能大于左括号数量例如:"())"是一种错误的括号组合。
第一条是对左括号的限制,第二点是对右括号的限制。
明白这两点之后,就知道我们需要知道当前已生成序列中左括号的数量,知道左括号数量,右括号数量也知道了,所以参数中加入一个int变量来记录左括号数量。每次判断前面两条是否满足,来决定是否添加左、右括号。
综上本题的6步策略:
1.状态变量:左括号数量
2.结束条件已生成长度==2n
3.选择列表:"(" , ")"
4.枝剪不符合上文两条的选项
5.递归下一层
6.撤销
代码
class Solution {
public:
vector<string> ans;
string s;
vector<string> generateParenthesis(int n) {
dfs(0,n);
return ans;
}
void dfs(int left_num,int n){ //s.size()-left_num=right_num
if(s.size()==2*n){
ans.push_back(s);
return;
}
if(left_num+1<=n){//左括号数量不大于n
s+="(";//选择
dfs(left_num+1,n);//递归
s.erase(s.size()-1,1);//撤销
}
if(s.size()-left_num < left_num){//右括号数量<左括号
s+=")";//选择
dfs(left_num,n);//递归
s.erase(s.size()-1, 1);//撤销
}
}
};