本题是继二叉树后第一次接触回溯算法的的第一个算法题。具体可看代码随想录 回溯算法。
class Solution {
//ArrayList和LinkedList区别
//前者基于数组实现,后者基于双链表实现
//前者对搜索元素复杂度O(1),而插入删除开销较大;后者相反
//但是如果在末尾插入元素,ArrayList需要开销相对较小,本题是直接在末尾添加path
//而且ArrayList的内存使用量较小,所以用ArrayList
List<List<Integer>> res = new ArrayList<>();
//这里用LinkedList是因为path中需要重复的添加删除操作,用LinkedList更合适
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combine(int n, int k) {
combine1(n, k, 1);
return res;
}
//本题startIndex表示当前开始的索引位置
private void combine1(int n, int k, int startIndex) {
//判断结束条件,可以放在for循环后面更好理解。当path长度和K相等时
//说明此时path已经符合条件了,所以放入结果集res,并跳出循环return,然后递归返回上一层。
if (path.size() == k) {
res.add(new ArrayList<>(path));
return;
}
//从开始索引位置循环,这里 n - (k - path.size()) + 1是剪枝优化后的结果,
//具体可看Carl代码随想录
for (int i = startIndex; i <= n - (k - path.size()) + 1; i ++) {
//将当前i放入path
path.add(i);
//递归操作,从当前元素的下一个索引位置开始
combine1(n, k, i + 1);
//回溯,当符合条件之后,需要删除当前元素,回溯回上一层递归
path.removeLast();
}
}
}