DFS与回溯专题:组合
题目链接: 77.组合
参考题解:代码随想录
题目描述
代码思路
该题为经典回溯问题,题目要求k个数来组合,则利用回溯来进行k层for循环。为了防止重复遍历,要用start参数标记每次for循环遍历的起点。
同时,循环停止的条件,最普通就是用题目给的n。然后参考代码随想录,发现是可以剪枝的。举个例子。n=10,k=5。一共需要组合5个数,当你已经确定了前3个数,那第4个数最多只能搜索到9,因为至少要留个10给第5个数。这样,每次循环时都可以进行剪枝。
代码纯享版
未剪枝的情况
class Solution {
public List<List<Integer>> list_all = new ArrayList();
public List<Integer> list = new ArrayList();
public List<List<Integer>> combine(int n, int k) {
backtrack(n, k, 1);
return list_all;
}
void backtrack(int n, int k, int start){
if(k == list.size()){
list_all.add(new ArrayList(list));
return;
}
for(int i = start; i <= n; i++){
list.add(i);
backtrack(n, k, i + 1);
list.remove(list.size() - 1);
}
}
}
剪枝后的情况
class Solution {
public List<List<Integer>> list_all = new ArrayList();
public List<Integer> list = new ArrayList();
public List<List<Integer>> combine(int n, int k) {
backtrack(n, k, 1);
return list_all;
}
void backtrack(int n, int k, int start){
if(k == list.size()){
list_all.add(new ArrayList(list));
return;
}
for(int i = start; i <= n - (k - list.size()) + 1; i++){
list.add(i);
backtrack(n, k, i + 1);
list.remove(list.size() - 1);
}
}
}
代码逐行解析版
class Solution {
public List<List<Integer>> list_all = new ArrayList(); //记录所有组合
public List<Integer> list = new ArrayList(); //记录某个组合
public List<List<Integer>> combine(int n, int k) {
backtrack(n, k, 1); //回溯
return list_all; //返回所有组合的列表
}
void backtrack(int n, int k, int start){
//当list中元素个数与题目要求的k个数相等时
if(k == list.size()){
list_all.add(new ArrayList(list)); //将list添加到list_all中,注意这里要new,进行浅拷贝
return;
}
//for循环遍历当前位所有可能添加到数
//start未每次循环后i+1,即后一位添加的数字必定比前一位大
//n - (k - list.size()) + 1为剪枝的结果:
//如果for循环选择的起始位置之后的元素个数 已经不足 我们需要的元素个数了,那么就没有必要搜索了。(参考代码随想录)
for(int i = start; i <= n - (k - list.size()) + 1; i++){
list.add(i); //将数字添加到list
backtrack(n, k, i + 1); //递归进行下一位的操作
list.remove(list.size() - 1); //回溯:删掉最后一个元素
}
}
}