77.组合
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]]
提示:
1 <= n <= 20
1 <= k <= n
思路
若是使用for循环嵌套组合1到n中的数,若k取大一点就会有非常多的嵌套for要写,明显是不合适的,所以这里引入了回溯的思想,直观的想法是画出递归树,这里引用了力扣大佬画的图
这里我们需要使用一个表示路径的栈path去记录已选择的数。
class Solution {
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res=new ArrayList<>();//答案
if(k<=0||n<k){
return res;
}
Deque<Integer> path=new ArrayDeque<>();
dfs(n,k,1,path,res);//题目要求从1开始
return res;
}
private void dfs(int n,int k,int begin,Deque<Integer> path,List<List<Integer>> res){
if(path.size()==k){//如果path长度已经=要寻找的数个数,说明找到一组答案
res.add(new ArrayList<>(path));
return;
}
for(int i=begin;i<=n-(k-path.size())+1;i++){//此处是一个优化思想,如果n=7,k=4,那么从5开始搜索已经没有意义了,所以搜索起点有上界。
path.addLast(i);//将当前节点记录进path
dfs(n,k,i+1,path,res);//将path传入下一次递归从i+1开始寻找
path.removeLast();//将该节点删除,代表了回溯的操作。
}
}
}
思路2.按照每一个数选与不选递归。
public class Solution {
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res = new ArrayList<>();
if (k <= 0 || n < k) {
return res;
}
// 为了防止底层动态数组扩容,初始化的时候传入最大长度
Deque<Integer> path = new ArrayDeque<>(k);
dfs(1, n, k, path, res);
return res;
}
private void dfs(int begin, int n, int k, Deque<Integer> path, List<List<Integer>> res) {
if (k == 0) {
res.add(new ArrayList<>(path));
return;
}
if (begin > n - k + 1) {
return;
}
// 不选当前考虑的数 begin,直接递归到下一层
dfs(begin + 1, n, k, path, res);
// 不选当前考虑的数 begin,递归到下一层的时候 k - 1,这里 k 表示还需要选多少个数
path.addLast(begin);
dfs(begin + 1, n, k - 1, path, res);
// 深度优先遍历有回头的过程,因此需要撤销选择
path.removeLast();
}
}
总结
回溯法第一题卡了好几天,需要加强复习。