77. 组合
给定两个整数 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]]
思考
首先,这题是个求所有解问题- 回溯法。
- 注意:[3,1][1,3]算一个。
假设n=4,k=2时,回溯问题都可以抽象成树状结构如下图。

回溯所需要的参数有
int startIndex = 1;
backTracking(n,k,startIndex ,list,lists);
- n [1,2,…n]
- k 抽中k个数
- startIndex 每次循环开始的坐标
- list 保存递归遍历的路径
- lists 保存结果返回
每次从集合中选取元素,可选择的范围随着选择的进行而收缩,调整可选择的范围,就是要靠startIndex。
从下图中红线部分可以看出,在集合[1,2,3,4]取1之后,下一层递归,就要在[2,3,4]中取数了,那么下一层递归如何知道从[2,3,4]中取数呢,靠的就是startIndex。

所以需要startIndex来记录下一层递归,搜索的起始位置。
- 回溯函数终止条件
什么时候到达所谓的叶子节点了呢?
即list.size() = k
list这个数组的大小如果达到k,说明我们找到了一个子集大小为k的组合了,在图中list存的就是根节点到叶子节点的路径。
把list保存起来,并终止本层递归。
所以终止条件代码如下:
if(list.size()==k){
lists.add(new ArrayList<>(list));
return;
}
- 单层搜索的过程
回溯法的搜索过程就是一个树型结构的遍历过程,在如下图中,可以看出for循环用来横向遍历****,递归的过程是纵向遍历。
for (int i = startindex; i <= n; i++) {
list.add(i);
backTracking(n,k,i+1,list,lists);
list.remove(list.size()-1);//回溯
}
可以看出backtracking(递归函数)通过不断调用自己一直往深处遍历,总会遇到叶子节点,遇到了叶子节点就要返回。
backtracking的下面部分就是回溯的操作了,撤销本次处理的结果。

如此我们才遍历完图中的这棵树。
for循环每次从startIndex开始遍历,然后用list保存取到的节点i。
package 力扣;
import java.util.ArrayList;
import java.util.List;
/**
* @author yyq
* @create 2022-04-26 9:07
*/
public class leetcode77 {
public static void main(String[] args) {
combine(3,2);
}
public static List<List<Integer>> combine(int n, int k) {
List<List<Integer>> lists=new ArrayList<>();
List<Integer> list=new ArrayList<>();
int startIndex = 1;
backTracking(n,k,1,list,lists);
return lists;
}
private static void backTracking(int n, int k, int startindex,List<Integer> list, List<List<Integer>> lists) {
if(list.size()==k){
lists.add(new ArrayList<>(list));
return;
}
for (int i = startindex; i <= n; i++) {
list.add(i);
backTracking(n,k,i+1,list,lists);
list.remove(list.size()-1);
}
}
}
回溯模板
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:**本层集合中元素(树中节点孩子的数量就是集合的大小)**) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}

542

被折叠的 条评论
为什么被折叠?



