77. 组合
方法:回溯
利用回溯解决问题就是暴力的方法,相当于遍历一颗多叉树,可以画出这颗树方便做题
class Solution {
private:
vector<vector<int>> ret; // 最终返回结果
vector<int> path; // 其中一种结果
public:
void backtracking(int n, int k, int startIndex) {
if (path.size() == k) {
ret.push_back(path);
return;
} // 递归终止条件
// 回溯法的搜索过程就是一个树型结构的遍历过程,
for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) {//剪枝优化
path.push_back(i); // 处理节点
backtracking(n, k, i + 1); // 递归
path.pop_back(); // 回溯,撤销处理的节点
} // for循环用来横向遍历,递归的过程是纵向遍历
}
vector<vector<int>> combine(int n, int k) {
backtracking(n, k, 1);
return ret;
}
};
216. 组合总和 III
方法:回溯
和上个题思路基本一样,这个题只使用数字1到9,for循环每层i<=9,path收集每次选取的元素,相当于树型结构里的边,sum来统计path里元素的总和
class Solution {
private:
vector<vector<int>> ret;
vector<int> path;
public:
void backtracking(int k, int n, int startIndex, int sum) {
if (sum > n) return;//剪枝
if (path.size() == k) {
if (sum == n)
ret.push_back(path);
return;
}
for (int i = startIndex; i <= 9 - (k - path.size()) + 1; i++) {
path.push_back(i);
backtracking(k, n, i + 1, sum + i);
path.pop_back();
}
}
vector<vector<int>> combinationSum3(int k, int n) {
backtracking(k, n, 1, 0);
return ret;
}
};
其中
backtracking(k, n, i + 1, sum + i);
隐含回溯,相当于以下代码
sum += i;
backtracking(k, n, i + 1, sum);
sum -= i;
17. 电话号码的字母组合
方法:回溯
- 首先用string letterMap[10],来做数字和字母的映射
- 确定回溯函数参数时的index是记录遍历第几个数字了,就是用来遍历digits的
- 确定终止条件:输入几个数字,递归深度就是几
- 单层遍历逻辑:首先要取index指向的数字,并找到对应的字符集,然后for循环来处理这个字符集
- 处理完一个字符集后去递归处理下一个数字对应的字符集
class Solution {
private:
string letterMap[10] = {"", "", "abc", "def", "ghi",
"jkl", "mno", "pqrs", "tuv", "wxyz"};
string s;
vector<string> ret;
public:
void backtracking(const string& digits, int index) {
if (index == digits.size()) {
ret.push_back(s);
return;
}
string letters = letterMap[digits[index] - '0'];
for (int i = 0; i < letters.size(); i++) {
s.push_back(letters[i]);
backtracking(digits, index + 1);
s.pop_back();
}
}
vector<string> letterCombinations(string digits) {
if (digits.size() == 0) return ret;
backtracking(digits, 0);
return ret;
}
};
感悟:
- 回溯法来解决n个for循环的问题
- 回溯问题一定要理解这个多叉树长什么样
- 回溯法的搜索过程就是一个树型结构的遍历过程,要明确for循环用来横向遍历,递归纵向遍历,宽度决定了每一层递归要回溯的次数(也就是有几个兄弟),深度决定了什么时候递归return