回溯
回溯法,一般可以解决如下几种问题:
组合问题:N个数里面按一定规则找出k个数的集合
切割问题:一个字符串按一定规则有几种切割方式
子集问题:一个N个数的集合里有多少符合条件的子集
排列问题:N个数按一定规则全排列,有几种排列方式
棋盘问题:N皇后,解数独等等
2. 组合
思考:
按顺序往后找,for循环从1->n,使用递归树的深度表示子集的大小
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<int> tmp;
findAnswer(tmp, 1, n, k);
return answer;
}
private:
void findAnswer(vector<int> &tmp, int start, int n, int depth) {
if (depth == 0) {
answer.push_back(tmp);
return ;
}
for (int i = start; i <= n; i++) {
if (flag[i]) continue;
flag[i] = true;
tmp.push_back(i);
findAnswer(tmp, i + 1, n, depth - 1);
tmp.pop_back();
flag[i] = false;
}
}
vector<vector<int>> answer;
bool flag[21];
};
3. 组合优化
还是上面这道题,考虑有没有可以剪枝的部分。
思路:
因为找组合的过程是从左向右遍历数组,当前搜索到数组某个位置,如果后面的数字全部选中也凑不够目标子集的大小,则递归树的该分支没必要再向下搜索了,因为一定没有答案。
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<int> tmp;
findAnswer(tmp, 1, n, k);
return answer;
}
private:
void findAnswer(vector<int> &tmp, int start, int n, int depth) {
if (depth == 0) {
answer.push_back(tmp);
return ;
}
for (int i = start; i <= n; i++) {
if (flag[i]) continue;
if (n - i + 1 < depth) continue; // 如果剩余的数全都要也凑不够子集的大小,则剪枝
flag[i] = true;
tmp.push_back(i);
findAnswer(tmp, i + 1, n, depth - 1);
tmp.pop_back();
flag[i] = false;
}
}
vector<vector<int>> answer;
bool flag[21];
};