回溯简单点看也就是深度优先搜索,在递归之前做出选择,递归之后撤销选择。
就比如LeetCode上的这道括号生成,n对括号,给出所有合法的括号字符串组合。
//暴力求解:
class Solution {
//合法的条件是,任意子串的左括号数量不会少于右括号的数量
bool isValid(const string& s){
int balance=0;
for(char iter : s){
if(iter == '(')
++balance;
else{
--balance;
if(balance<0)
return false;
}
}
return balance == 0;
}
//递归所有组合
void generateAll(string& track, int n, vector<string>& res){
if(track.size() == n){
//当长度达到最大时,判断这个组合是否为合法字符串
if(isValid(track))
res.push_back(track);
return ;
}
track += '(';
generateAll(track, n, res);
track.pop_back();
track += ')';
generateAll(track, n, res);
track.pop_back();
}
public:
vector<string> generateParenthesis(int n) {
vector<string> res;
string track;
generateAll(track, 2*n, res);
return res;
}
};
暴力求解会生成许多不合法的字符串去判断是否合法,所以在这一步上可以进行 优化,思路是只对暂时合法的组合继续添加元素(或)。
class Solution{
//left左括号的数量
//right右括号的数量
void followTrack(vector<string>& res, string& track, int left, int right, int n){
if(2*n == track.size()){
res.push_back(track);
return ;
}
//n对括号最多只有n个左括号
if(left<n){
track +='(';
followTrack(res, track, left+1, right, n);
track.pop_back();
}
//合法的前提是右括号的数量不多于左括号
if(right<left){
track +=')';
followTrack(res, track, left, right+1, n);
track.pop_back();
}
}
public:
vector<string> generateParenthesis(int n) {
vector<string> res;
string track;
followTrack(res, track, 0, 0, n);
return res;
}
};