216.组合总和Ⅲ
题目链接:216.组合总和Ⅲ
难度指数:😀😐😕
如果把 组合问题理解了,本题就容易一些了。
最朴素、暴力的想法:k是多少,就嵌套几层for循环
回溯算法也是暴力的方式,和之前嵌套几个for循环的思路是一样的,只不过回溯算法通过递归的方式帮我们控制for循环的嵌套层数。
任何用回溯算法解决的问题,都可以抽象成一个树形结构。
本题k控制着树的深度(满足k就行,再往下递归就没意义了),9(因为整个集合就是9个数)就是树的宽度(for循环的范围)。
代码思路:
- 一维数组path,收集单条路径,符合要求的结果
- 二维数组result,(符合题目描述要求的结果可能不止一个),把符合题目要求的path都放进result
递归函数的参数和返回值:
本题的递归函数不需要我们返回东西,因为结果都放在全局变量。
确定参数:1️⃣ targetSum,将要求的组合的和; 2️⃣ k; 3️⃣ Sum收集这条路径已有的和 4️⃣ startIndex
最后
targetSum和Sum作比较,若相同就是符合题目要求的组合
startIndex ,我们每次递归的时候,会把这次要搜索的起始位置传进来,(最开始调用递归函数的时候,传进去的startIndex是1)
void backtracking(targetSum, k, Sum, startIndex) {
//终止条件
if (path.size() == k) { //k控制着树的深度
if (targetSum == Sum) { //若相等,这个就是一个目标组合
result.push_back(path); //放进result
}
}
//单层递归的逻辑
for (int i = startIndex; i <= 9; i++) {
Sum += i;
path.push_back(i);
backtracking(targetSum, k, Sum, i + 1);
sum -= i;
path.pop_back(); //回溯
}
}
AC代码: (核心代码模式)
剪枝:
代码思路:
void backtracking(targetSum, k, Sum, startIndex) {
//一个剪枝:
if (Sum > targetSum) {
return;
}
//终止条件
if (path.size() == k) { //k控制着树的深度
if (targetSum == Sum) { //若相等,这个就是一个目标组合
result.push_back(path); //放进result
}
}
//单层递归的逻辑
//for循环条件里有另一个剪枝:(对i的边界上的控制)
for (int i = startIndex; i <= 9 - (k - path.size()) + 1; i++) {
Sum += i;
path.push_back(i);
backtracking(targetSum, k, Sum, i + 1);
sum -= i;
path.pop_back(); //回溯
}
}
for循环条件里有另一个剪枝:(对i的边界上的控制)
有点难理解
如果 k = 2 ,那么 i 只需要遍历到8就可以了。
if (Sum > targetSum) { return; } 也可以放这里:
void backtracking(targetSum, k, Sum, startIndex) {
//终止条件
if (path.size() == k) { //k控制着树的深度
if (targetSum == Sum) { //若相等,这个就是一个目标组合
result.push_back(path); //放进result
}
}
//单层递归的逻辑
for (int i = startIndex; i <= 9; i++) {
Sum += i;
path.push_back(i);
if (Sum > targetSum) {
return;
}
backtracking(targetSum, k, Sum, i + 1);
sum -= i;
path.pop_back(); //回溯
}
}
AC代码: (核心代码模式)
class Solution {
private:
vector<vector<int>> result; //存放结果集
vector<int> path; //存放符合条件的结果
void backtracking(int targetSum, int k, int sum, int startIndex) {
//一个剪枝:
if (sum > targetSum) {
return;
}
//终止条件
if (path.size() == k) { //k控制着树的深度
if (targetSum == sum) { //若相等,这个就是一个目标组合
result.push_back(path);
}
}
//单层递归的逻辑
//for循环里有另一个剪枝:(对i的边界上的控制)
for (int i = startIndex; i <= 9 - (k - path.size()) + 1; i++) {
sum += i;
path.push_back(i);
backtracking(targetSum, k, sum, i + 1);
sum -= i; //回溯
path.pop_back(); //回溯
}
}
public:
vector<vector<int>> combinationSum3(int k, int n) {
result.clear();
path.clear();
backtracking(n, k, 0, 1);
return result;
}
};
17.电话号码的字母组合
题目链接:17.电话号码的字母组合
难度指数:😀😕🙁
本题大家刚开始做会有点难度,先自己思考20min,没思路就直接看题解。
可以用map做映射,也可以用一个二维数组来做映射
例如:输入:"23",抽象为树形结构,如图所示:
这棵树的深度由我们输入数字的个数来控制;树的宽度由每个数字所对应的字母的长度来控制。
收获结果的地方都是在叶子节点
代码思路:
string s; 存放单个结果
vector<string> result; 单个结果放进结果集
注意这个index可不是 77.组合 和 216.组合总和Ⅲ中的 startIndex。
这个 index 是记录遍历第几个数字了,就是用来遍历digits的(题目中给出数字字符串),同时index 也表示树的深度。
index仅仅是表示在递归中,传入的字符串遍历到哪个数字了。
(上一道题的 startIndex 是告诉我们:我们之前已经收获了哪些元素,避免我们得到重复的组合,)
而本题是在两个集合中,将两个集合的每个元素做一个组合。(本题并不需要 startIndex 帮我们做去重的操作 )
string s;
vector<string> result;
void backtracking(digits, index) {
//终止条件:
if (index == digits.size()) { //说明遍历到头了 (index从0开始)
result.push_back(s);
return;
}
//单层递归的逻辑:
int digit = dights[index] - '0'; //eg:字母2变成数字2
string letters = letterMap[digit]; //取数字对应的字符集
for (int i = 0; i < letters.size(); i++) {
s.push_back(letters[i]);
backtracking(digits, index + 1);
s.pop_back(); //回溯
}
}
AC代码: (核心代码模式)
class Solution {
private:
const string letterMap[10] = {
"", //0
"", //1
"abc", //2
"def", //3
"ghi", //4
"jkl", //5
"mno", //6
"pqrs", //7
"tuv", //8
"wxyz", //9
};
public:
string s; //存放单个结果
vector<string> result; //将单个结果放进结果集
void backtracking(const string& digits, int index) {
//终止条件
if (index == digits.size()) { //说明遍历到头了 (注:index从0开始)
result.push_back(s);
return;
}
//单层递归的逻辑:
int digit = digits[index] - '0'; //eg:字母2变成数字2
string letters = letterMap[digit]; //取数字对应的字符集
for (int i = 0; i < letters.size(); i++) {
s.push_back(letters[i]);
backtracking(digits, index + 1); //递归
s.pop_back(); //回溯
}
}
public:
vector<string> letterCombinations(string digits) {
s.clear();
result.clear();
if (digits.size() == 0) {
return result;
}
backtracking(digits, 0);
return result;
}
};