39.组合总和
思路
组合总和、组合总和2、组合总和3可以一起理解,将三者的区别区分开!!!
此题没有规定组合长度,并且每个数据可以重复利用,所以结束的唯一条件就是:当累加和与目标值相同时,将小集合加入到大集合中,记住需要每次更新小集合。
此方法参数要设置一个sum来累加总和,接下来开始遍历数组,设置startIndex作为开头,将遍历到的数据加入到小集合中,然后要累加sum,之后进行递归,这样注意:每个数据都可以重复使用;所以递归的位置也是从当前开始即为:i,之后记得回溯继续找下一个合适的小集合,回溯后需要将sum减去对应的值。
注意
此题与组合总和不一样的地方在于:小集合长度没有限制;同个数据可以重复利用;所以变化在于判断条件少了长度限制这一项,而递归的开始变成了当前位置。
实现代码
class Solution {
List<List<Integer>> res=new ArrayList<List<Integer>>();
List<Integer> list=new ArrayList<Integer>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
backTracking(candidates,target,0,0);
return res;
}
public void backTracking(int [] candidates,int target,int sum,int startIndex){
if(sum>target){
return;
}
if(sum==target){
res.add(new ArrayList<>(list));
return;
}
for(int i=startIndex;i<candidates.length;i++){
list.add(candidates[i]);
sum+=candidates[i];
backTracking(candidates,target,sum,i);
list.remove(list.size()-1);
sum-=candidates[i];
}
}
}
40.组合总和2
思路
难点:此题中的数组里面的数据可能是重复的,所以会存在最后得出的组合集合是重复的,所以如何去重就成了关键与难点!
此题的结束条件是:当累加总和与目标值相同时就将小集合加入到大集合中,注意要每次更新。
每个数据不能重复使用,那需要设置参数startIndex来控制范围,递归在每次的下一个开始;关于将遍历数据加入集合然后进行的递归回溯都是和之前题目一样的;
关于去重:在代码随想录里面介绍了两种去重方法:1.树层去重、2.树枝去重。
本题利用了树层,首先需要设置一个int数组used,used的长度与给出的数据数组的长度是一致的,用来记录数据数组对应位置的数据是否被使用,使用的赋值为1,反之为0;
用来组合的数据构成了树的宽度,树层去重是:首先要遍历的数据一定先要排好序,然后在遍历到树层中出现了与之前相同的数据,就会跳过现在遍历的数据;如何跳过呢?这里用了一个很巧妙的方法:就是当前后两个数据相同时,当记录到前一个数据的used对应为0时,就跳过当前数据;
前一个数据的used对应为0表示:已经遍历完前一个并且回溯了,现在是到下一个数据的意思,所以当前就是下一个,而前后相同,所以将当前这个“后”跳过;
注意
关注使用“树层去重”的方法和used数组的使用;
树层去重的前提是数组一定要排好序!!!
实现代码
class Solution {
List<List<Integer>> res=new ArrayList<List<Integer>>();
List<Integer> list=new ArrayList<Integer>();
int [] used;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
used=new int[candidates.length];
//一定先要排序!!!因为要去重,所以相同的元素是要在一起的!!!
Arrays.sort(candidates);
backTracking(candidates,target,0,0);
return res;
}
public void backTracking(int [] candidates,int target,int sum,int startIndex){
if(sum>target){
return;
}
if(sum==target){
res.add(new ArrayList<Integer>(list));
return;
}
for(int i=startIndex;i<candidates.length;i++){
//此步骤是在树层去除重复的操作
if(i>0&&candidates[i]==candidates[i-1]&&used[i-1]==0){
continue;
}
list.add(candidates[i]);
sum+=candidates[i];
used[i]=1;
backTracking(candidates,target,sum,i+1);
list.remove(list.size()-1);
sum-=candidates[i];
used[i]=0;
}
}
}
131.分割回文串
思路
回文串的概念:从前后读取都是相同的,所以判断是否是回文数的方法是:字符串从前后开始遍历一直到中间,若遍历到内容均同,表示是回文数,若当出现不同的马上返回false。
判断结束条件,当遍历到字符串的最后一个的后面时,就结束遍历,将小集合加入到大集合。
注意参数的设置,接下来遍历字符串,然后用判断回文数的方法对字符串是从哪开始到哪结束的这一段进行判断,若是回文数,就将这一段用substring切割下来,并且加入到集合中;若不是就跳过当前。之后要进行递归,是才下一个数据开始。
注意
1.可以将此题转化为二叉树去理解,字符串一个个的分开变成树层,判断切割位置;
2.当遍历到字符串的最后一个元素的后面时,表示已经遍历且切割完成。
实现代码
class Solution {
List<List<String>> res=new ArrayList<List<String>>();
List<String> list=new ArrayList<String>();
public List<List<String>> partition(String s) {
backTracking(s,0);
return res;
}
//判断字串是否是回文数
public boolean judge(String s,int start,int end){
for(int i=start,j=end;i<j;i++,j--){
if(s.charAt(i)!=s.charAt(j)){
return false;
}
}
return true;
}
//startIndex是控制切割位置的
public void backTracking(String s,int startIndex){
if(startIndex==s.length()){
res.add(new ArrayList<>(list));
return;
}
for(int i=startIndex;i<s.length();i++){
//先判断某部分是否是回文数,若是就要将此切割放入集合中
if(judge(s,startIndex,i)){
String str=s.substring(startIndex,i+1);
list.add(str);
}else{
continue;
}
backTracking(s,i+1);
list.remove(list.size()-1);
}
}
}
总结
1.一定要理解关注:树层去重的方法;
2.要掌握如何“分割回文串”里运用到的方法。