1--回溯算法理论基础
回溯算法本质上是一个暴力搜索的过程,其常用于解决组合、切割、子集、排列等问题,其一般模板如下:
void backTracking(参数){if(终止条件){// 1. 收获结果;// 2. return;}for(..遍历){// 1. 处理节点// 2. 递归搜索// 3. 回溯 // 即撤销对节点的处理}return;
}
2--组合问题
主要思路:
基于回溯法,暴力枚举 k 个数,需注意回溯弹出元素的操作;
#include <iostream>
#include <vector>class Solution {
public:std::vector<std::vector<int>> combine(int n, int k) {std::vector<int> path;backTracking(n, k, path, 1); // 从第 1 个数开始return res;}void backTracking(int n, int k, std::vector<int> path, int start){if(path.size() == k){res.push_back(path);return;}for(int i = start; i <= n; i++){path.push_back(i);backTracking(n, k, path, i + 1); // 递归暴力搜索下一个数path.pop_back(); // 回溯}}private:std::vector<std::vector<int>> res;
};int main(int argc, char* argv[]){int n = 4, k = 2;Solution S1;std::vector<std::vector<int>> res = S1.combine(n, k);for(auto v : res){for(int item : v) std::cout << item << " ";std::cout << std::endl;}return 0;
}
3--组合问题的剪枝操作
上题的组合问题中,对于进入循环体 for(int i = start; i <= n; i++):
已选择的元素数量为:path.size()
仍然所需的元素数量为:k - path.size()
剩余的元素集合为:n - i + 1
则为了满足要求,必须满足:k-path.size() <= n - i + 1,即 i <= n - k + path.size() + 1
因此,可以通过以下条件完成剪枝操作:
for(int i = start; i <= i <= n - k + path.size() + 1; i++)
#include <iostream>
#include <vector>class Solution {
public:std::vector<std::vector<int>> combine(int n, int k) {std::vector<int> path;backTracking(n, k, path, 1); // 从第 1 个数开始return res;}void backTracking(int n, int k, std::vector<int> path, int start){if(path.size() == k){res.push_back(path);return;}for(int i = start; i <= n - k + path.size() + 1; i++){path.push_back(i);backTracking(n, k, path, i + 1); // 暴力下一个数path.pop_back(); // 回溯}}private:std::vector<std::vector<int>> res;
};int main(int argc, char* argv[]){int n = 4, k = 2;Solution S1;std::vector<std::vector<int>> res = S1.combine(n, k);for(auto v : res){for(int item : v) std::cout << item << " ";std::cout << std::endl;}return 0;
}
4--组合总和III
主要思路:
类似于上面的组合问题,基于回溯来暴力枚举每一个数,需要注意剪枝操作;
#include <iostream>
#include <vector>class Solution {
public:std::vector<std::vector<int>> combinationSum3(int k, int n) {std::vector<int> path;backTracking(k, n, 0, path, 1);return res;}void backTracking(int k, int n, int sum, std::vector<int>path, int start){if(sum > n) return; // 剪枝if(path.size() == k){ // 递归终止if(sum == n){res.push_back(path);}return;}for(int i = start; i <= 9 + path.size() - k + 1; i++){ // 剪枝path.push_back(i);sum += i;backTracking(k, n, sum, path, i + 1); // 递归枚举下一个数// 回溯sum -= i;path.pop_back();}}
private:std::vector<std::vector<int>> res;
};int main(int argc, char* argv[]){int k = 3, n = 7;Solution S1;std::vector<std::vector<int>> res = S1.combinationSum3(k, n);for(auto v : res){for(int item : v) std::cout << item << " ";std::cout << std::endl;}return 0;
}
5--电话号码的字母组合
主要思路: