第七章 回溯算法part02
39. 组合总和
本题是 集合里元素可以用无数次,那么和组合问题的差别 其实仅在于 startIndex上的控制
题目链接/文章讲解:https://programmercarl.com/0039.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8C.html
视频讲解:https://www.bilibili.com/video/BV1KT4y1M7HJ
40.组合总和II
本题开始涉及到一个问题了:去重。
注意题目中给我们 集合是有重复元素的,那么求出来的 组合有可能重复,但题目要求不能有重复组合。
题目链接/文章讲解:https://programmercarl.com/0040.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8CII.html
视频讲解:https://www.bilibili.com/video/BV12V4y1V73A
131.分割回文串
本题较难,大家先看视频来理解 分割问题,明天还会有一道分割问题,先打打基础。
https://programmercarl.com/0131.%E5%88%86%E5%89%B2%E5%9B%9E%E6%96%87%E4%B8%B2.html
视频讲解:https://www.bilibili.com/video/BV1c54y1e7k6
39. 组合总和
题目链接
解题思路
startIndex 问题,题目说了这个数可以重复,由原来组合问题的 startIndex 传入i+1 变为 startIndex 传入 i 就是当前数 i 可以重复选取,i之前的数仍然不能用,否则会出现重复的组合,这和组合问题是一样的。
剪枝一定要在数组排序的情况下,当前sum+candidates[i]大于target了
code
class Solution {
/**
*
* 本题还需要startIndex来控制for循环的起始位置,对于组合问题,什么时候需要startIndex呢?
* 我举过例子,如果是一个集合来求组合的话,就需要startIndex,例如:77.组合 (opens new window),216.组合总和III (opens new window)。
* 如果是多个集合取组合,各个集合之间相互不影响,那么就不用startIndex,例如:17.电话号码的字母组合
**/
/**
* 回溯三部曲
* 1.归函数参数
* 2.递归终止条件
* 3.单层搜索逻辑
**/
List<List<Integer>> res=new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates);//优化剪枝 对数组排序
backtracking(candidates,target,0,0,path,res);
return res;
}
public void backtracking(int[] candidates,int target,int startIndex,int sum){
if(sum>target){
return;
}
if(sum==target){
res.add(new ArrayList<>(path));
return;
}
for (int i = startIndex; i < candidates.length; i++) {
//优化剪枝,在对数组排序的前提下,如果当前元素大于目标值,则后续元素肯定大于目标值,直接剪枝 target=8 [2,3,4]不满足 那么[2,3,5]更不满足了
if(sum + candidates[i]> target) break;
path.add(candidates[i]);
sum+=candidates[i];
backtracking(candidates,target,i,sum,path,res);//这里startIndex是i,表示可以重复使用
path.remove(path.size()-1);
sum-=candidates[i];
}
}
}
40.组合总和II
题目链接
https://leetcode.cn/problems/combination-sum-ii/description/
解题思路
这题相比上题,要考虑重复的问题,即出现相同元素,那么就要考虑到排序,排序后,如果后一个元素和前一个元素重复,那这个后一个元素就应该跳过,否则会出现重复的组合。
code
class Solution {
List<List<Integer>> res=new ArrayList<>();
List<Integer> path =new ArrayList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
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<>(path));
return;
}
for(int i=startIndex;i<candidates.length;i++){
if(sum+candidates[i]>target){
break;
}
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.remove(path.size()-1);
}
}
}
131.分割回文串
题目链接
https://leetcode.cn/problems/palindrome-partitioning/description/
解题思路
跟组合题目及其相似,只不过变为了分割元素,startIndex就是分割点, [startIndex,i] 就是分割的子串 s.substring(startIndex,i+1) substring方法是左闭有开, 每分割一次判断这个子串是不是回文,使用双指针判断
注意:最后一次收集结果的时候是进行i+1 所以终止条件是 startIndex==s.length() 而不是s.length()-1
code
class Solution {
List<List<String>> res=new ArrayList<>();
List<String> path=new ArrayList<>();
public List<List<String>> partition(String s) {
backtracking(s,0);
return res;
}
public void backtracking(String s,int startIndex){
if(startIndex==s.length()){
res.add(new ArrayList<>(path));
return;
}
for(int i=startIndex;i<s.length();i++){
String backStr = s.substring(startIndex,i+1);
//判断是否是回文
if(!isBackStr(backStr)){
continue;
}
path.add(backStr);
backtracking(s,i+1);
path.remove(path.size()-1);
}
}
public boolean isBackStr(String backStr){
if(null==backStr || "".equals(backStr)){
return false;
}
int left=0;
int right=backStr.length()-1;
while(left<=right){
if(backStr.charAt(left)!=backStr.charAt(right)){
return false;
}
left++;
right--;
}
return true;
}
}