携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情
highlight: a11y-dark theme: channing-cyan
题目描述
给定两个整数 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]]
提示:
- 1 <= n <= 20
- 1 <= k <= n
> 题目元素
- 给定两个正整数,n、k。n代表有1。。。。n的数;
- k代表需要从这n个数中找到k个组合成数组;
- 返回所有有可能的组合方式
题目解析
将n个数进行排列组合,组合大小为k。排列组合的题一般采用回溯+递归进行解答。
-
回溯:记录当前路径path,每次做完选择将当前选择放入path中,一条路径选择完成之后,退回到上次选择的节点,重新选择另外一条路径。
回溯包含三个重要元素
- 1、 选择路径: 已经做出的选择;
- 2、 选择列表: 当前可以做的选择(还未选过的元素);
- 3、 终止条件: 达到决策数底层,无法再做选择。
- 首先确定本题的三要素,假定数组path为选择路径;
- 选择列表就是原始数组中未被选择过的数组;
- 终止条件就是path.length=k。
确定了三要素,接下来可以模拟一下代码流程。 n=4,k=2。则原始的数组为[1,2,3,4]。设原始的path=[]
- 第一次选择为1,第二次选择为2,path=[1,2],此时path.length=k,所以此次递归调用直接return,选择回退;
- 撤销第二次选择的2,选择3,此时path=[1,3],同理,此时path.length=k,此次递归也return,选择继续回退;
- 撤销第二次选择的3,选择4,此时path[1,4],此时不仅path.length=k,而且选择列表也为空,所以这次选择直接结束,进入下次循环。
代码实现
/**
*
* @param n
* @param k
* @return
*/
public static List<List<Integer>> combine(int n, int k) {
// 创建选择结果
List<List<Integer>> res = new ArrayList<>();
// k为0或者可选数组长度小于k时直接返回
if (k == 0 || n < k) {
return res;
}
// 创建当前路径
ArrayDeque<Integer> path = new ArrayDeque<Integer>();
// 从1开始遍历
findPath(res,path,1,n,k);
return res;
}
/**
* 递归获取路径
* @param res 放所有满足条件的路径
* @param path 已选择的路径
* @param i 当前遍历节点
* @param n n
* @param k k
*/
private static void findPath(List<List<Integer>> res, ArrayDeque<Integer> path, int i, int n, int k) {
if (path.size() == k) {
// 注意这里放入结果中的应该是个新数组,直接放入path引入的是path对象,会被后续操作改变
res.add(new ArrayList<>(path));
return;
}
for (;i <= n;i++) {
// 做选择
// 将当前元素放到队列末端
path.addLast(i);
findPath(res,path,i+1,n,k);
// 回溯
path.removeLast();
}
}