「这是我参与11月更文挑战的第22天,活动详情查看:2021最后一次更文挑战」
77. 组合
题目描述
给定两个整数 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]]
复制代码
解析
回溯法就是使用暴力法枚举出所有的可能,递归回溯相辅相成,回溯可以看成一棵树。以本题为例,树的深度为 n , 宽度为 k 。所以横向遍历的次数为 k , 纵向遍历的次数为 n 。
回溯三部曲:
- 1、确定递归函数的参数与返回值
- 求组合数,所以不考虑元素的顺序
- 使用一个数组
vector<int> path
存放每次遍历的结果 - 使用数组
vector<vector<int>> result
存放path
- 使用
int startIndex
来标记递归的每一层遍历开始的索引
- 2、确定终止条件
- 如果
path
已经存入了k
个元素,则终止此次遍历
- 如果
- 3、确定单层递归的逻辑
- 每层遍历时,将遍历的元素加入到
path
中 - 进行回溯
- 每层遍历时,将遍历的元素加入到
代码
class Solution
{
public:
vector<vector<int>> combine(int n, int k)
{
backtracking(n, k, 1);
return result;
}
private:
// 使用两个全局变量存放结果
// 存放最终的结果
vector<vector<int>> result;
// 存放单层递归的结果
vector<int> path;
// 回溯算法
// startIndex用来控制开始遍历的索引,遍历的范围逐渐缩小
void backtracking(int n, int k, int startIndex)
{
// 递归的终止条件
// 当path的 大小等于 k 时,将path数据存入到 结果数组中
if (path.size() == k)
{
result.push_back(path);
return;
}
// 单层递归逻辑
for (int i = startIndex; i <= n; i++)
{
path.push_back(i); // 处理节点
// 递归处理,控制树的纵向遍历
backtracking(n, k, i + 1);
path.pop_back(); // 回溯,撤销处理的节点
}
}
};
复制代码
剪枝操作
在单层for循环中,如果剩余的元素不足以和已经选取的元素组成K个,那么后面的遍历就是不必要的
- 已经选取元素个数:
path.size()
- 还需要选取元素个数:
k - path.size()
- 所以,遍历的起始索引必须满足
startIndex<=n - (k - path.size())+1
修改后的for
循环:
for(int i = startIndex;i<=n-(k-path.size())+1;i++)
{
... ...
}
复制代码
216. 组合总和 III
题目描述
找出所有相加之和为 n 的 k 个数的组合 **。 **组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:
- 所有数字都是正整数。
- 解集不能包含重复的组合。
示例 1:
输入: k = 3, n = 7
输出: [[1,2,4]]
复制代码
示例 2:
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
复制代码
解析
与上面的组合题思路相似,这里是求
k
个数的和等于n
,在判断条件上稍作改动。具体内容查看代码
class Solution
{
public:
vector<vector<int>> combinationSum3(int n, int k)
{
backtracking(n, k, 1);
return result;
}
private:
vector<vector<int>> result;
vector<int> path;
// 使用减法逐步减小n的值
void backtracking(int n, int k, int startIndex)
{
// 剪枝操作
if (n < 0)
{
return;
}
// 如果path的大小满足条件,并且path中的和为 n
if (path.size() == k && n == 0)
{
result.push_back(path);
return;
}
for (int i = startIndex; i <= 9; i++)
{
// 每次遍历到一个数,就将n的值减去这个数
n -= i;
// 将 i 添加到 path 中
path.push_back(i);
// 递归处理
backtracking(n, k, i + 1);
n += i; // 回溯
path.pop_back(); // 回溯
}
}
};
复制代码