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);
}
}
}