仅用于学习交流,未经许可不可转载。本文转载引用内容仅代表作者本人的观点,与本博客立场无关。
LeetCode216组合总和
代码随想录: 回溯算法——组合总和
力扣216: 组合总和Ⅲ
初印象
- 找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
- 只使用数字1到9
- 每个数字 最多使用一次
- 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
- 2 <= k <= 9;1 <= n <= 60
- 回溯函数返回值与输入参数,二维数组vector<vector> result。和77题一样借助一个path。 输入参数n, k
- 回溯函数终止条件 path.size=k如果和等于n,返回result
- 回溯函数遍历过程与LeetCode77题类似,抽象成树形结构,for循环1到9横向遍历,k决定树的深度,子树中一个startindex继续取数。
- 剪枝操作:取最小的数后总和大于n。为什么剪枝之前要先把回溯做了? for循环剪枝怎么操作
- 回溯,撤销处理结果在代码中如何体现?就是sum-=i;
path.pop_back();
看视频讲解后的想法
- 为什么剪枝之前要先把回溯做了?
- 剪枝分别从和的角度、元素个数够不够取的角度剪枝
- 从元素个数的角度,for循环剪枝怎么操作?path中已经有path.size个元素,还需要k-path.size个元素,i至多遍历到k-path.size+1。因为是小于等于所以要加一
- 从和的角度可以把下列代码放到函数开头
if (sum > tagetsum) return;
实现过程中遇到的问题
剪枝版本
class Solution {
private:
vector<vector<int>> result;//二维数组存放符合条件的结果的集合
vector<int> path;//存放符合条件结果
void backtracking(int targetsum, int k, int sum, int startIndex) {
//int sum,int startIndex自己从public中传进去
if (sum > targetsum) {
return;//总和大于目标和剪枝
}
if (path.size() == k) {//size后面加括号,这是终止条件
if (sum == targetsum) {
result.push_back(path);
}
return;//在这里return,如果path已经达到了k
}
for (int i = startIndex; i <= 9 - (k - path.size()) + 1; i++) {//剪枝,不剪枝为i<=9;
path.push_back(i);
sum += i;
backtracking(targetsum, k, sum, i + 1);//这里是i+1,不是startIndex+1
sum -= i;//回溯,先减去再退栈
path.pop_back();//这里括号里不能加i
}
}
public:
vector<vector<int>> combinationSum3(int k, int n) {
result.clear();
path.clear();
backtracking(n, k, 0, 1);
return result;
}
};
- line 5:这个sum和startIndex自己从public部分中传进去相当于全局变量。target和k的顺序不能改变不知道为什么
- line 8: 放在开头的总和剪枝
- line14:在这里return不要在上面的sum==targetsum里return
- line16:剪枝,不剪枝就填i<=9;
- line20-21:这俩顺序无所谓,但先减再退栈是好习惯,退栈pop_back不能加操作数
LeetCode17电话号码组合
代码随想录: 回溯算法——电话号码的字母组合
力扣17: 电话号码的字母组合
初印象
-
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
-
定义一个二维数组实现数字与字母之间的映射
-
题目给了一个string digits,需要一个digitIndex记录树的深度
-
C++ const关键字用法: 参考Mi ronin的博客——C++ const的用法详解。const修饰函数形参;函数体内不能修改形参a的值。
-
将index指向的数字转为int? index用法不太懂,下一层index为什么+1
看视频讲解后的想法
- index的用法,表示在digit字符串中已经遍历到的位置。因为是从不同的数字字母映射组合中取数,不用startIndex那种逻辑。
- 终止条件是index==digit.size()而不是digit.size()-1即指向最后一位。因为当指向最后一位时,最后一位仍要对应字母仍有处理过程, 不是真正的结束。
- int digit=digits[index]-‘0’。从字符串中的数字变成真正的数字。
- string letterMap[10]={…}数组中的每个元素是字符串,算是个二维数组。也可以用map来做。
C++ map关键字用法: 参考不会编程的小猿的博客——C++ 中map详解。map是STL的一个关联容器,以键值对存储的数据,其类型可以自己定义,每个关键字在map中只能出现一次,关键字不能修改,值可以修改 - index+1移动处理位置。
- 树形结构
实现过程中遇到的问题
class Solution {
private:
const string digitsToLetter[10] = {//const表示不可修改
"",//0
"",//1
"abc",//2
"def",//3
"ghi",//4
"jkl",//5
"mno",//6
"pqrs",//7
"tuv",//8
"wxyz",//9
};//注意内部格式,使用双引号,中间用逗号隔开
public:
vector<string> result;
string s;
void backtracking(const string&digits,int index) {
//const string&digits,这个写法需要确认一下
if (index==digits.size()) {//注意是==
result.push_back(s);
return;
}
int digit = digits[index] - '0';//获取当前数字,将其转换为真正的数字
string letters = digitsToLetter[digit];//完成数字与字母之间的转换
for (int i = 0; i < letters.size(); i++) {//不是小于等于
s.push_back(letters[i]);
backtracking(digits, index + 1);//index+1移动到digits下一位
s.pop_back();
}
}
vector<string> letterCombinations(string digits) {
s.clear();
result.clear();
if (digits.size() == 0) {
return result;
}//力扣有空串的测试用例,index初始为0,会不停压入空串
backtracking(digits, 0);
return result;
}
};
- line 3:const和string数组的用法
- line 15: public由此开始
- line18:这个写法需要确认
- line20:判断条件是==而非=
- line24:字符串数字与真正的数字区别是什么
- line26:不是小于等于,size本身是逻辑上从1开始,i从0开始
- line35:力扣有空串测试用例
总结
对回溯算法和组合问题有了更近一步的认识,这两道题总体不难。但是,c++的语法还需熟悉。