目录
一、39. 组合总和
本题搜索的过程抽象成树形结构如下:
注意图中叶子节点的返回条件,因为本题没有组合数量要求,仅仅是总和的限制,所以递归没有层数的限制,只要选取的元素总和超过target,就返回!
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res=new ArrayList<>();
Arrays.sort(candidates);
backtracking(res,new ArrayList<>(),candidates,target,0,0);
return res;
}
public void backtracking(List<List<Integer>> res, List<Integer> path, int[] candidates, int target, int sum, int startIndex)
{
if(sum==target)
{
res.add(new ArrayList<>(path));
return;
}
for(int i=startIndex;i<candidates.length;i++)
{
if(sum+candidates[i]>target) break;
path.add(candidates[i]);
backtracking(res,path,candidates,target,sum+candidates[i],i);
path.remove(path.size()-1);
}
}
}
二、40.组合总和II
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明: 所有数字(包括目标数)都是正整数。 解集不能包含重复的组合。
示例 1: 输入: candidates = [10,1,2,7,6,1,5], target = 8, 所求解集为: [ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6] ]
示例 2: 输入: candidates = [2,5,2,1,2], target = 5, 所求解集为: [ [1,2,2], [5] ]
本题的难点在于区别2中:集合(数组candidates)有重复元素,但还不能有重复的组合。
选择过程树形结构如图所示:
class Solution {
LinkedList path=new LinkedList<>();
List<List<Integer>> ans=new ArrayList<>();
boolean[] used;
int sum=0;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
used=new boolean[candidates.length];
Arrays.fill(used,false);
Arrays.sort(candidates);
backtracking(candidates,target,0);
return ans;
}
private void backtracking(int[] candidates, int target,int startIndex)
{
if(sum==target)
{
ans.add(new ArrayList(path));
}
for(int i=startIndex;i<candidates.length;i++)
{
if(sum+candidates[i]>target)
{
break;
}
if(i>0&&candidates[i]==candidates[i-1]&&!used[i-1])
{
continue;
}
used[i]=true;
sum+=candidates[i];
path.add(candidates[i]);
backtracking(candidates,target,i+1);
used[i]=false;
sum-=candidates[i];
path.removeLast();
}
}
}
三、131.分割回文串
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。
示例: 输入: "aab" 输出: [ ["aa","b"], ["a","a","b"] ]
所以切割问题,也可以抽象为一棵树形结构,如图:
递归用来纵向遍历,for循环用横向遍历,切割线(就是图中的红线)切割到字符串的结尾位置,说明找到了一个切割方法。
此时可以发现,切割问题的回溯搜索的过程和组合问题的回溯搜索的过程是差不多的。
class Solution {
List<List<String>>lists=new ArrayList<>();
Deque<String>deque=new LinkedList<>();
public List<List<String>> partition(String s) {
backTracking(s,0);
return lists;
}
private void backTracking(String s,int startIndex)
{
if(startIndex>=s.length())
{
lists.add(new ArrayList<>(deque));
return;
}
for(int i=startIndex;i<s.length();i++)
{
if(isPalindrome(s,startIndex,i))
{
String str=s.substring(startIndex,i+1);
deque.addLast(str);
}
else
{
continue;
}
backTracking(s,i+1);
deque.removeLast();
}
}
private boolean isPalindrome(String s,int startIndex,int end)
{
for(int i=startIndex,j=end;i<j;i++,j--)
{
if(s.charAt(i)!=s.charAt(j))
return false;
}
return true;
}
}
回溯还是很难