组合总和
注意:本题的特色是没有数量要求,可以无限重复,但是有总和的限制,所以间接的也是有个数的限制。
因为没有组合数量要求,所以递归层数无限制
回溯三部曲:
- 递归函数参数
首先,全局变量两个 result和path
然后题目中所给的参数 集合candidates 目标值target
还可以定义int型的sum
还需要startIndex来控制for循环的起始位置
2. 递归终止条件
两种情况:1. sum==target 收集结果
2. sum>target 返回
3. 单层搜索的逻辑
注意:因为本题元素可以重复选取,所以不需要i+1
剪枝优化
sum>target 时其实没有必要进入下一层递归
所以for循环可以进行优化 i<candidates.length&&sum+candidates[i]<=target
其他注意点:
本题还需要先进行排序(无重复元素),所以要Arrays.sort()
比如集合【2,2,3】与【2,3,2】与【3,2,2】其实是一个集合,所以递归遍历是i而不是startIndex
完整代码如下:
class Solution {
List<List<Integer>> result=new ArrayList<>();
LinkedList<Integer> path=new LinkedList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates);
backtracking(candidates,target,0,0);
return result;
}
private void backtracking(int[] candidates,int target,int sum,int startIndex){
if(sum>target) return;
if(sum==target){
result.add(new ArrayList<>(path));
return;
}
for(int i=startIndex;i<candidates.length&&sum+candidates[i]<=target;i++){
sum+=candidates[i];
path.add(candidates[i]);
backtracking(candidates,target,sum,i);
sum-=candidates[i];
path.removeLast();
}
}
}
组合总和||
本题与上题的区别在于:
- 本题candidates中的每个数字在每个组合中只能使用一次
- 本题数组candidates的元素是有重复的,而上题元素无重复
本题难点:集合有重复元素,但不能有重复组合 即使用过的的元素不能重复选取(即去重)
使用过在树形结构上有两个维度:1.同一树枝上使用过 2.同一树层上使用过
本题需要去重的是同一树层上的重复元素,同一树枝上的都是一个组合里的元素,不需要去重。注意:树层去重需要对数组排序。
回溯三部曲
- 递归函数参数
两个全局变量 result和path
在上题基础上需要多定义一个bool型数组,记录同一树枝上元素是否使用过
2. 递归终止条件
与上题相同
3. 单层搜索的逻辑
排序后 若candidates【i】=-candidates【i-1】并且used【i-1】==false说明同一树层使用过
- used[i - 1] == true,说明同一树枝candidates[i - 1]使用过
- used[i - 1] == false,说明同一树层candidates[i - 1]使用过
其实根本不需要used数组去重,直接用startIndex来去重也可以,
即当i>startIndex&&candidates[i]==candidates[i-1]时,对同一树层使用过的元素进行跳过
完整代码如下:
class Solution {
List<List<Integer>> result =new ArrayList<>();
LinkedList<Integer> path =new LinkedList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
backtracking(candidates,target,0,0);
return result;
}
private void backtracking(int[] candidates,int target,int sum,int startIndex){
if(sum==target){
result.add(new ArrayList<>(path));
return;
}
for(int i=startIndex;i<candidates.length&&sum+candidates[i]<=target;i++){
if(i>startIndex&&candidates[i]==candidates[i-1]){
continue;
}
sum+=candidates[i];
path.add(candidates[i]);
backtracking(candidates,target,sum,i+1);
sum-=candidates[i];
path.removeLast();
}
}
}
分割回文串
回文串:正着读和反着读都一样的字符串。
本题涉及两个关键问题:
- 切割问题,有不同的切割方式
- 判断回文
其实切割问题类似组合问题
回溯三部曲
- 递归函数参数
两个全局变量 result和path
还需要定义int型startIndex
2. 递归函数终止条件
切割线切到字符串最后面则终止条件
什么是切割线呢?
传入的startIndex就是切割线
3. 单层搜索的逻辑
首先判断这个字串是不是回文,若是则加入path
注意切割过的位置,不能重复切割,所以,backtracking(s, i + 1); 传入下一层的起始位置为i + 1。
这里就需要定义一个方法来判断是否是回文字串
使用双指针法判断
还需要注意 截取字符串substring()是一个左闭右开的区间
完整代码如下:
class Solution {
List<List<String>> result=new ArrayList<>();
LinkedList<String> path=new LinkedList<>();
public List<List<String>> partition(String s) {
backtracking(s,0);
return result;
}
private void backtracking(String s,int startIndex){
if(startIndex>=s.length()){
result.add(new ArrayList<>(path));
return;
}
for(int i=startIndex;i<s.length();i++){
if(decidehuiwen(s,startIndex,i)){
String str=s.substring(startIndex,i+1);
path.add(str);
}else{
continue;
}
backtracking(s,i+1);
path.removeLast();
}
}
public boolean decidehuiwen(String s,int startIndex,int m){
for(int i=startIndex,j=m;i<j;i++,j--){
if(s.charAt(i)!=s.charAt(j)){
return false;
}
}
return true;
}
}