39. 组合总和
本题是 集合里元素可以用无数次,那么和组合问题的差别 其实仅在于 startIndex上的控制
题目链接/文章讲解:代码随想录
视频讲解:带你学透回溯算法-组合总和(对应「leetcode」力扣题目:39.组合总和)| 回溯法精讲!_哔哩哔哩_bilibili
import java.util.*;
class Solution {
LinkedList<Integer> path = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
back(target, 0, 0, candidates);
return res;
}
public void back(int target, int index, int sum, int[] candidates) {
// 如果当前和等于目标值,加入当前的组合
if (sum == target) {
res.add(new LinkedList<>(path)); // 新建一个副本,防止修改
return;
}
// 如果当前和大于目标值,返回
if (sum > target) {
return;
}
// 从当前索引开始遍历,允许重复选择同一数字
for (int i = index; i < candidates.length; i++) {
// 选择当前数字
path.add(candidates[i]);
// 递归调用,允许继续选择当前数字
back(target, i, sum + candidates[i], candidates);
// 回溯,撤销选择
path.removeLast();
}
}
}
因为数字可以重复使用,在再次调用回溯算法的参数列表里面,i保持不变,表示可以重复选择,记住要把数组传递进来
40.组合总和II
本题开始涉及到一个问题了:去重。
注意题目中给我们 集合是有重复元素的,那么求出来的 组合有可能重复,但题目要求不能有重复组合。
题目链接/文章讲解:代码随想录
给定一个候选人编号的集合 candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
candidates
中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
示例 1:
输入: candidates =[10,1,2,7,6,1,5]
, target =8
, 输出: [ [1,1,6], [1,2,5], [1,7], [2,6] ]
视频讲解:回溯算法中的去重,树层去重树枝去重,你弄清楚了没?| LeetCode:40.组合总和II_哔哩哔哩_bilibili
class Solution {
LinkedList<Integer>path=new LinkedList<>();
List<List<Integer>>res=new ArrayList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);//对数组排序,便于剪枝和避免重复
back(candidates,target,0,0);
return res;}
public void back(int []candidates,int target,int index,int sum)
{
if(sum==target)
{
res.add(new LinkedList<>(path));
return;
}
if(sum>target)
{
return;
}
for(int i=index;i<candidates.length;i++)
{
if(i>index&&candidates[i]==candidates[i-1])continue;//跳过当前i
path.add(candidates[i]);
back(candidates,target,i+1,sum+candidates[i]);
path.removeLast();
}
}
}
要想去重,首先得对数组排序,再加上关键语句
if(i>index&&candidates[i]==candidates[i-1])continue;//跳过当前i
可以回顾一下之前的三数之和,四数之和的剪枝去叶操作。
131.分割回文串
本题较难,大家先看视频来理解 分割问题,明天还会有一道分割问题,先打打基础。
给你一个字符串 s
,请你将 s
分割成一些 子串,使每个子串都是 回文串 。返回 s
所有可能的分割方案。
示例 1:
输入:s = "aab" 输出:[["a","a","b"],["aa","b"]]
示例 2:
输入:s = "a" 输出:[["a"]]
视频讲解:带你学透回溯算法-分割回文串(对应力扣题目:131.分割回文串)| 回溯法精讲!_哔哩哔哩_bilibili
class Solution {
List<List<String>>res=new ArrayList<>();
LinkedList<String>path=new LinkedList<>();
public List<List<String>> partition(String s) {
back(s,0);
return res;
}
public void back(String s,int index)
{
if(index==s.length())//表示所有的子串都已经分割
{
res.add(new LinkedList<>(path));
return;
}
for(int i=index;i<s.length();i++)
{
String m=s.substring(index,i+1);//不断分割字符串,起点索引和终点索引,左闭右开
if(ishuiwen(m)){
path.add(m);
back(s,i+1);
path.removeLast();//集合使用size()
}
}}
//java中方法中不能嵌套方法
public boolean ishuiwen(String s)
{
int left=0;
int right=s.length()-1;
while(left<right)
{
if(s.charAt(left)!=s.charAt(right))
{return false;}
left++;
right--;
}
return true;
}
}
注意点:在回溯函数中,注意什么时候使用index,什么时候使用i
s.substring(i,j),i为起始索引,j为终点索引,左闭右开
难点部分和优化