难度中等953收藏分享切换为英文接收动态反馈
给定两个整数 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
代码思路
对于递归函数dfs,步骤如下:
- 如果当前组合已满
k
个数,将这个组合加入最终结果,退出。 - 如果已经访问过所有的数,退出。
- 可行性剪枝处理:如果接下来的数都放入当前的状态数组,也不足
k
个,退出。 - 假设当前的数
pos
加入数组,进行下一步递归,结束后,回溯,弹出数组末端的数。 - 假设当前的数
pos
不加入数组,直接进行下一步递归。 - 递归结束。
复杂度分析
设待选的最大的数为N
,要选的数为K
。
C(N, K)
代表N
个数中挑选出K
个不同数的组合数个数。
时间复杂度
N
层的满二叉树的状态共2^N
个,未剪枝的情况下时间复杂度为O(2^N)
。
- 剪枝后可以减少大量的搜索节点,时间复杂度可达到
O(C(N, K))
。
空间复杂度
- 递归的空间复杂度取决于搜索树的最大深度,最大深度为
N
,空间复杂度为O(N * K)
。
class Solution {
public List<List<Integer>> combine(int n, int k) {
List<Integer> combination = new ArrayList<Integer>();
List<List<Integer>> combinations = new ArrayList<List<Integer>>();
dfs(1,combination,n,k,combinations);
return combinations;
}
// pos代表已访问到哪个数,combination代表当前地组合
public void dfs(int pos,List<Integer> combination,int n,int k,List<List<Integer>> combinations) {
// 第一个递归出口,如果满足 k 个,将这种组合加入最终结果中
if(combination.size() == k) {
combinations.add(new ArrayList(combination));
return;
}
// 第二个递归出口,如果以访问完1 ~ n所有数字,退出当前函数
if(pos > n ) {
return;
}
// 可行性剪枝,如果后面的数都放入,个数不足k的话,退出
if(combination.size() + n - pos + 1 < k) {
return;
}
combination.add(pos);
dfs(pos+1,combination,n,k,combinations);
combination.remove(combination.size() - 1);//id
dfs(pos+1,combination,n,k,combinations);
}
}