Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.
For example,
If n = 4 and k = 2, a solution is:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
一刷
题解:经典的backtracking
这里branching factor是n, depth是k。 所以其实时间复杂度是 O(n! / k!) 约等于 O(n!)
Time Complexity - O(2^n), Space Complexity - O(n)。
public class Solution {
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res = new ArrayList<>();
if(n<1 || k<1) return res;
combine(res, n, k, 1, new ArrayList<Integer>());
return res;
}
private void combine(List<List<Integer>> res, int n, int k, int pos, List<Integer> list){
if(list.size() == k){
res.add(new ArrayList<Integer>(list));
return;
}
for(int i=pos; i<=n; i++){
list.add(i);
combine(res, n, k, ++pos, list);
list.remove(list.size()-1);
}
}
}
注意,曾经将递归表达式写作 combine(res, n, k, pos+1, list);
会得到完全不一样的结果,比如会出现[[1,2],[1,3],[2,2],[2,3],[3,2],[3,3]], 原因是,如果不自加,当1出栈后,pos还是1,i为2,也就是当压入2后,调用 combine(res, n, k, 2, list); 而不是combine(res, n, k, 3, list)。会引入[2,2]这种结果,所以pos需要自加。因为pos决定递归后加入的数值范围,所以也可以写为:
public class Solution {
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res = new ArrayList<>();
if(n<1 || k<1) return res;
combine(res, n, k, 1, new ArrayList<Integer>());
return res;
}
private void combine(List<List<Integer>> res, int n, int k, int pos, List<Integer> list){
if(list.size() == k){
res.add(new ArrayList<Integer>(list));
return;
}
for(int i=pos; i<=n; i++){
list.add(i);
combine(res, n, k, i+1, list);
list.remove(list.size()-1);
}
}
}
二刷
public class Solution {
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res = new ArrayList<>();
List<Integer> list = new ArrayList<>();
dfs(1, n, k, res, list);
return res;
}
private void dfs(int from, int nums, int size, List<List<Integer>> res, List<Integer> list){
if(list.size() == size){
res.add(new ArrayList<>(list));
return;
}
for(int i=from; i<=nums; i++){
list.add(i);
dfs(i+1, nums, size, res, list);
list.remove(list.size()-1);
}
}
}