算法笔记3
通过之前题目的我们对于使用回溯法解决组合问题已经有了一个大致的方向,本题同样是一道组合总和问题,结合前两题的思路,未剪枝的代码是可以有思路写出来的。
题目描述
初看题目就很容易发现本题像是之前做过的两道题的综合,既可以看到相加之和n,也看到了记录数字个数的k,那么就可以通过三部曲来解题了。
解题过程
1.选择参数:在参数选择上,除了题目中给定的表示组合总和的n与表示每条路径有几个数字的k,还需要规定初始位置的参数startIndex,记录路径的一维数组path与记录结果的二维数组result,至此参数选择完毕。
2.确定终止条件:在第一天的组合问题中,我们知道path用来记录每个组合的路径,path的大小就等于每条路径有几个数字,所以第一个条件就是当path的大小等于k,这时就可以记录结果,除了这个条件我们还要满足加和的条件,即sum是否等于目标值n,满足以上两个条件就可以用result数组收集最终结果。为了代码的健壮性,我们也可以提前判断sum大于n的情况直接跳出就好了。
if(sum >= n) {
return;
}
if(path.size() == k && sum == n) {
result.push_back(path);
return;
}
3.循环递归回溯:for循环里面我们需要考虑的是判断条件,题目中说数字只在1-9之间去取,所以直接简单设置为i <= 9或者i < 10即可。下面就是与算法笔记2一样常规的收集路径,加和每一项的值,进行递归操作,题目中没有说每个数字可以重复使用,所以我们的第三个参数设置为i + 1。最后就是回溯操作,之前怎么操作就怎么回退就好了,先拿sum减去之前加的内容以及path吐出之前的元素。
for(int i = startIndex; i <= 9; i++) {
path.push_back(i);
sum += i;
backtracking(k, n, i + 1);
sum -= i;
path.pop_back();
}
}
完整代码
class Solution {
public:
vector<int> path;
vector<vector<int>> result;
int sum;
void backtracking(int k, int n, int startIndex) {
if(sum >= n) {
return;
}
if(path.size() == k && sum == n) {
result.push_back(path);
return;
}
for(int i = startIndex; i <= 9; i++) {
path.push_back(i);
sum += i;
backtracking(k, n, i + 1);
sum -= i;
path.pop_back();
}
}
vector<vector<int>> combinationSum3(int k, int n) {
backtracking(k, n, 1);
return result;
}
};
剪枝优化
这里优化思想可以完全参考1.1的做法,即n - (k - path.size()) + 1,参考下来仿写一下就可以了,即i <= 9 - (k - path.size()) + 1。
class Solution {
public:
vector<int> path;
vector<vector<int>> result;
int sum;
void backtracking(int k, int n, int startIndex) {
if(sum >= n) {
return;
}
if(path.size() == k && sum == n) {
result.push_back(path);
return;
}
for(int i = startIndex; i <= 9 - (k - path.size()) - 1; i++) {
path.push_back(i);
sum += i;
backtracking(k, n, i + 1);
sum -= i;
path.pop_back();
}
}
vector<vector<int>> combinationSum3(int k, int n) {
backtracking(k, n, 1);
return result;
}
};
结语
今天的题目比较轻松,算是作为之前几次内容的复习巩固。