77. 组合 - 力扣(LeetCode)
给定两个整数 n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。可以按 任何顺序 返回答案。
示例 1:
输入:n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
示例 2:
输入:n = 1, k = 1
输出:[[1]]
(一)了解回溯算法
- for 循环遍历集合区间,可以理解为一个节点有多少个孩子,这个for循环就执行多少次。
- backtracking(递归)自己调用自己。
- 实现递归由上图可以看出,for循环可以理解是横向遍历,backtracking 就是纵向遍历
(二)backtracking + 图解
递归三步曲:
- ① 首先,确定递归函数的参数和返回值
- ② 确定递归的终止条件
- ③ 确定单层搜索的逻辑
void backtracking(参数) {
if(终止条件) {
存放结果;
return;
}
for(选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表);//递归
回溯,撤销处理结果
}
}
- 一维数组:path 用来收集路径
- 二维数组:result 存放path,作为结果集
- startIndex 用来控制每次搜索的位置
class Solution {
public:
vector<int> path; // 用来存放符合条件单一结果
vector<vector<int>> result; // 存放符合条件结果的集合
void bactracking(int n,int k,int startIndex) {
// 回溯函数终止条件
if(path.size() == k) {
// 用result二维数组,把path保存起来,并终止本层递归
result.push_back(path);
return;
}
// 单层搜索的过程
for(int i=startIndex;i<=n;i++) { // 控制树的横向遍历
path.push_back(i); // 处理节点
bactracking(n,k,i+1); // 递归:控制树的纵向遍历,注意下一层搜索要从 i + 1 开始
path.pop_back(); // 回溯,撤销处理的节点
}
}
vector<vector<int>> combine(int n, int k) {
bactracking(n,k,1);
return result;
}
};
(三)剪枝操作
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking(int n, int k, int startIndex) {
if (path.size() == k) {
result.push_back(path);
return;
}
for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) { // i为本次搜索的起始位置(优化的地方)
path.push_back(i); // 处理节点
backtracking(n, k, i + 1);
path.pop_back(); // 回溯,撤销处理的节点
}
}
public:
vector<vector<int>> combine(int n, int k) {
backtracking(n, k, 1);
return result;
}
};
参考和推荐文章、视频:
代码随想录 (programmercarl.com)https://www.programmercarl.com/0077.%E7%BB%84%E5%90%88.html#%E6%80%9D%E8%B7%AF带你学透回溯算法-组合问题(对应力扣题目:77.组合)| 回溯法精讲!_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1ti4y1L7cv/?vd_source=a934d7fc6f47698a29dac90a922ba5a3