回溯算法
- 回溯和递归密不可分, 一定需要使用递归实现回溯的过程
- 回溯是纯暴力的搜索, 解决暴力搜索for也无法实现的问题
- 解决的问题
- 组合
- 切割
- 子集
- 排列
- 棋盘问题
- 理解: 抽象为树形结构
- 集合的大小就构成了树的宽度(每层的横向都为单层递归的子集合, 用for循环遍历)
- 递归的深度就构成了树的深度(下图为3层递归)
77. 组合
给定两个整数 n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。
你可以按 任何顺序
返回答案。
参考
分析
- 第一层递归的元素为: 1 2 3 4 , 则第二层为 2 3 4 , 第三层为 3,4 , 第四层为 4
- 若满足递归的次数, 则会返回
class Solution {
public:
vector<vector<int>> result;
vector<int> result_tem;
//由 result_tem 容器的大小判断递归次数
void myoperator(int n, int k, int index) {
if (result_tem.size() == k) {
result.push_back(result_tem);
return;
}
for (int i = index; i <= n; i++) {
//剪枝操作
if (tem.size() + (n - i) + 1 < k) break;
result_tem.push_back(i);
myoperator(n, k, i + 1);
result_tem.pop_back();
}
return;
}
//等价操作, 由 k 判断递归的次数
void myoperator(int n, int k, int index) {
if (k == 0) {
result.push_back(tem);
return;
}
for (int i = index; i <= n; i++) {
tem.push_back(i);
myoperator(n, k-1 , i + 1);
tem.pop_back();
}
return;
}
vector<vector<int>> combine(int n, int k) {
myoperator(n, k, 1);
return result;
}
};
剪枝操作
- 1 2 3 4, k = 4 时
- i = 2 时, 还剩下 4 - 2 + 1 个元素可以增加 (即 n - i + 1个元素)
- 所以 path.size() + (n - i + k) < k时, 不够提供元素数量, 使用该方法剪枝
216. 组合总和 III
参考
//已剪枝
class Solution {
public:
vector<vector<int>> res;
vector<int> res_tem;
void myoperator (int k, int n, int index, int sum) {
//递归层数
if (k == res_tem.size()) {
if (sum == n) {
res.push_back(res_tem);
}
return;
}
//遍历每层元素
for (int i = index; i <= 9; i++) {
//剪枝操作
if (res_tem.size() + (9 - index + 1) < k) break;
if (sum > n) break;
res_tem.push_back(i);
myoperator(k, n, i + 1, sum + i);
res_tem.pop_back();
}
}
vector<vector<int>> combinationSum3(int k, int n) {
myoperator(k, n, 1, 0);
return res;
}
};
//未剪枝
class Solution {
public:
vector<vector<int>> result;
vector<int> result_tem;
void myoperator(int k, int n, int index) {
if (result_tem.size() == k) {
int sum = 0;
for (int i = 0; i < k; i++) sum += result_tem[i];
if (sum == n) result.push_back(result_tem);
return;
}
for (int i = index; i <= 9; i++) {
result_tem.push_back(i);
myoperator(k, n, i + 1);
result_tem.pop_back();
}
}
vector<vector<int>> combinationSum3(int k, int n) {
myoperator(k, n, 1);
return result;
}
};
- 未剪枝
- 剪枝
17. 电话号码的字母组合
补充: string操作
class Solution {
public:
vector<string> mystr={
"",
"",
"abc",
"def",
"ghi",
"jkl",
"mno",
"pqrs",
"tuv",
"wxyz"
};
vector<string> res;
string res_tem;
void myoperator(string digits, int index) {
if (digits == "") return;
if (index == digits.length()) {
res.push_back(res_tem);
return;
}
char tem1 = digits[index];// '2' ~ '9'
string tem2 = mystr[tem1 - '0'];
for (int i = 0; i < tem2.length(); i++) {
res_tem.push_back(tem2[i]);
myoperator(digits, index + 1);
res_tem.pop_back();
}
}
vector<string> letterCombinations(string digits) {
myoperator(digits, 0);
return res;
}
};
class Solution {
public:
// map<string, string> my={
// {"2","abc"},
// {"3","def"},
// {"4","ghi"},
// {"5","jkl"},
// {"6","mno"},
// {"7","pqrs"},
// {"8","tuv"},
// {"9","wxyz"}
// };
map<char, string> my={
{'2',"abc"},
{'3',"def"},
{'4',"ghi"},
{'5',"jkl"},
{'6',"mno"},
{'7',"pqrs"},
{'8',"tuv"},
{'9',"wxyz"}
};
vector<string> res;
string res_tem;
void myoperator(string digits, int index) {
if (digits == "") return;
if (index == digits.length()) {
res.push_back(res_tem);
return;
}
char tem1 = digits[index]; // char 不能直接赋值给 string, 可使用push_back的方法赋值
string tem2 = my[tem1];
for (int i = 0; i < tem2.length(); i++) {
res_tem.push_back(tem2[i]);
myoperator(digits, index + 1);
res_tem.pop_back();
}
}
vector<string> letterCombinations(string digits) {
myoperator(digits, 0);
return res;
}
};