代码随想录算法训练营第二十四天 | 回溯算法 77.组合
回溯算法理论基础
- 回溯是递归的副产品,只要有回溯就会有递归
- 回溯的本质是琼剧,所以效率不高
回溯法可以解决的问题
- 组合问题
- 切割问题
- 子集问题
- 排列问题
- 棋盘问题
如何理解回溯
- 回溯算法的问题都可以抽象为树形结构
- 集合的大小就构成了书的快读,递归的深度就构成了树的深度
回溯法模板
回溯三部曲
-
回溯函数返回值及参数
返回值一般为void,参数通常不固定,先写逻辑需要什么参数再传什么参数void backtracking(参数)
-
回溯函数终止条件
一般来说搜索到叶子节点就可以回溯了if(终止条件) { 存放结果; return; }
-
回溯搜索的遍历过程
for(选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) { 处理节点; backtracking(路径,选择列表); // 递归 回溯,撤销处理结果 }
-
整体框架
void backtradking(参数) { if(终止条件) { 存放结果; return; } for(选择:本层集合中的元素(树中节点孩子的数量就是集合的大小)) { 处理节点; backtracking(路径,选择列表); // 递归 回溯,撤销处理结果 } }
77.组合
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<int> path;
vector<vector<int>> ans;
backtracking(n, k, path, ans, 1);
return ans;
}
void backtracking(int n, int k, vector<int>& path, vector<vector<int>>& ans, int startIndex) {
// 终止条件
if(path.size() == k) {
ans.push_back(path);
return ;
}
for(int i = startIndex; i <= n; ++i) {
// 处理节点
path.push_back(i);
// 递归
backtracking(n, k, path, ans, i + 1);
// 回溯
path.pop_back();
}
}
};
剪枝优化
思路:当集合中剩余的元素不够还需要的元素个数时,应该终止遍历
还需要:k - path.size()
剩余:n - i + 1;
n - i + 1 >= k -path.size() --> i <= n - k + path.size() + 1;
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<int> path;
vector<vector<int>> ans;
backtracking(n, k, path, ans, 1);
return ans;
}
void backtracking(int n, int k, vector<int>& path, vector<vector<int>>& ans, int startIndex) {
// 终止条件
if(path.size() == k) {
ans.push_back(path);
return ;
}
// 修改循环终止条件进行剪枝
for(int i = startIndex; i <= n - k + path.size() + 1; ++i) {
// 处理节点
path.push_back(i);
// 递归
backtracking(n, k, path, ans, i + 1);
// 回溯
path.pop_back();
}
}
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构