吾日三省吾身
还记得的梦想吗
正在努力实现它吗
可以坚持下去吗
力扣题号:77. 组合 - 力扣(LeetCode)
下述题目描述和示例均来自力扣
题目描述
给定两个整数 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]]
提示:
1 <= n <= 20
1 <= k <= n
思路
Java解法一:回溯算法
我们利用递归,在递归之中回溯,挨个的寻找我们所需要的目标。找到一个之后就删除最后那一个,这就是一个回溯的操作,然后再找。具体代码如下:
java
class Solution {
// 先创建两个全局变量
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combine(int n, int k) {
// 进入递归搜索
backtracking(n, k, 1);
// 此时已经递归回溯完毕,结果已经保存到了res中直接返回即可
return res;
}
public void backtracking(int n, int k, int startIndex) {
// 递归结束条件就是当前的元素集已经达到了k个
if (path.size() == k){
// 由于我们的path是linkedlist类型,所以要保存到res中要先转换乘Arraylist
res.add(new ArrayList<>(path));
// 终止
return;
}
// 进入寻找过程
for (int i = startIndex; i <= n ; i++) {
path.add(i);
backtracking(n, k, i + 1);
// 删除最后一个元素,回溯
path.removeLast();
}
}
}
拿下~!!!!
Java解法一:回溯算法剪枝操作
在回溯算法的开篇就提到了,回溯是一个暴力的枚举过程,唯一的优化方式也就是进行剪枝操作,非常幸运的是这道题就可以进行一个剪枝的操作。
思考一些问题。如果起始位置之后的元素个数已经小于我们需要的元素的个数,我们还需要在这个分支上浪费时间吗?答案显然是不需要了。
大概的优化思路如下(参考于《代码随想录》):
(1)已经选择的元素个数:path.size()
(2)还需要元素的个数: k - path.size()
(3)最多从起始位置 (n - ( k - path.size() )+ 1 ) 开始遍历
因此我们也只需要改变一下for循环的结束条件这么一处代码即可
java
class Solution {
// 先创建两个全局变量
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combine(int n, int k) {
// 进入递归搜索
backtracking(n, k, 1);
// 此时已经递归回溯完毕,结果已经保存到了res中直接返回即可
return res;
}
public void backtracking(int n, int k, int startIndex) {
// 递归结束条件就是当前的元素集已经达到了k个
if (path.size() == k){
// 由于我们的path是linkedlist类型,所以要保存到res中要先转换乘Arraylist
res.add(new ArrayList<>(path));
// 终止
return;
}
// 进入寻找过程
// 进行剪枝操作
for (int i = startIndex; i <= (n - (k - path.size()) + 1) ; i++) {
path.add(i);
backtracking(n, k, i + 1);
// 删除最后一个元素,回溯
path.removeLast();
}
}
}
总结
组合问题是回溯算法的典型题目。可以通过剪枝的方法来优化我们的回溯代码~~
ヾ( ̄▽ ̄)Bye~Bye~