学习内容
力扣算法
77.组合
xxxxxx
具体内容
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]]
做题思路
求组合是不包括重复的,而回溯一般用二叉树,如下图所示
解题
class Solution {
//存放结果集的数组
List<List<Integer>> result = new ArrayList<>();
//存放满足条件的结果
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combine(int n, int k) {
backtracking(n,k,1);
return result;
}
//target表示下一次递归从哪里开始,可以理解为是对横向的处理
public void backtracking(int n,int k,int target){
if(path.size() == k){
result.add(new ArrayList(path));
}
for(int i = target;i <= n;i++){
//处理节点
path.add(i);
//递归
//i是对纵向的处理
backtracking(n,k,i+1);
//回溯
path.removeLast();
}
}
}
解答代码存在的疑问
1、为什么是i+1而不是target+1
backtracking(n,k,i+1);
因为target是记录本层递归的集合从哪里开始遍历,抽象的看他是横向的,而i是纵向的,是从某个位置开始,再不断向后遍历
2.为什么是result.add(new ArrayList(path)),而不是result.add(path);
result.add(new ArrayList(path));
res.add(new ArrayList(path)):开辟一个独立地址,地址中存放的内容为path集合,后续path的变化不会影响到res。
res.add(path):将res尾部指向了path地址,后续path内容的变化会导致res的变化。
剪枝优化
当n=4时,k=4时,就会发现只需要经历一次for循环就可以,无需再进行后续的操作,因为当n取2时,后面总的元素已经不够k取值,即题目给出的元素要大于k值才会往下操作
集合仍需要的元素个数:k-path.size()
剩余的元素:n-(k-path.size())
整合可得到:i <=n-(k-path.size())
由于是从1开始的,即后面要加1,右闭区间 i <=n-(k-path.size())+1
class Solution {
//存放结果集的数组
List<List<Integer>> result = new ArrayList<>();
//存放满足条件的结果
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combine(int n, int k) {
backtracking(n,k,1);
return result;
}
//target表示下一次递归从哪里开始,可以理解为是对横向的处理
public void backtracking(int n,int k,int target){
if(path.size() == k){
result.add(new ArrayList(path));
}
//剪枝优化
for(int i = target;i <= n-(k-path.size())+1;i++){
//处理节点
path.add(i);
//递归
//i是对纵向的处理
backtracking(n,k,i+1);
//回溯
path.removeLast();
}
}
}
216.组合总和III
找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
只使用数字1到9
每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
示例 1:
输入: k = 3, n = 7 输出: [[1,2,4]] 解释: 1 + 2 + 4 = 7 没有其他符合的组合了。
示例 2:
输入: k = 3, n = 9 输出: [[1,2,6], [1,3,5], [2,3,4]] 解释: 1 + 2 + 6 = 9 1 +
3 + 5 = 9 2 + 3 + 4 = 9 没有其他符合的组合了。
示例 3:
输入: k = 4, n = 1 输出: [] 解释: 不存在有效的组合。
在[1,9]范围内使用4个不同的数字,我们可以得到的最小和是1+2+3+4 = 10,因为10 > 1,没有有效的组合。
做题思路
该问题与上述问题思路一致,只不过是多了个和为 n的条件,且提供的数也是1-9
解题
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combinationSum3(int k, int n) {
backTracking(n, k, 1, 0);
return result;
}
private void backTracking(int targetSum, int k, int startIndex, int sum) {
// 减枝
if (sum > targetSum) {
return;
}
if (path.size() == k) {
if (sum == targetSum) result.add(new ArrayList<>(path));
return;
}
// 减枝 9 - (k - path.size()) + 1
for (int i = startIndex; i <= 9 - (k - path.size()) + 1; i++) {
path.add(i);
sum += i;
backTracking(targetSum, k, i + 1, sum);
//回溯
path.removeLast();
//回溯
sum -= i;
}
}
}
解答代码存在的疑问
1、return、continue、break区别
(1)return关键字并不是专门用于跳出循环的,return的功能是结束一个方法。 一旦在循环体内执行到一个return语句,return语句将会结束该方法,循环自然也随之结束。与continue和break不同的是,return直接结束整个方法,不管这个return处于多少层循环之内。
(2)break用于完全结束一个循环,跳出循环体。不管是哪种循环,一旦在循环体中遇到break,系统将完全结束循环,开始执行循环之后的代码。
(3)continue的功能和break有点类似,区别是continue只是中止本次循环,接着开始下一次循环。而break则是完全中止循环。
17.电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例:
输入:"23" 输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
做题思路
(1)首先要创建对应的数组,来表示电话号码所包含的字母
(2)获取题目输入所对应的字符串,获得数字,再获得相对应的字符串
(3)之后再利用回溯的思想,按上述题目的方法解题
解题
class Solution {
//存放结果的结果集
List<String> result = new ArrayList<>();
public List<String> letterCombinations(String digits) {
if(digits.length() == 0 || digits == null){
return result;
}
//初始对应所有的数字,为了直接对应2-9,新增了两个无效的字符串""
String[] numString = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
backtracking(digits,0,numString);
return result;
}
//每次迭代获取一个字符串,所以会设计大量的字符串拼接,所以这里选择更为高效的 StringBuild
StringBuilder temp = new StringBuilder();
public void backtracking(String digits,int num, String[] numString){
//终止条件 当num刚好是digits的长度
if(num == digits.length()){
result.add(temp.toString());
return;
}
//获得题目相对应字符的位置
int nums = digits.charAt(num) - '0';
//获得对应的字符串
String str = numString[nums];
for(int i = 0;i < str.length(); i++) {
temp.append(str.charAt(i));
//c
backtracking(digits, num + 1,numString);
//剔除末尾的继续尝试
temp.deleteCharAt(temp.length() - 1);
}
}
}