回溯的题目,常见的有排列,组合,子集,分割,棋盘等,就是将所有的情况都列举出来,加上一些好一点的剪枝操作,套路就是用一个方法然后回溯所有情况,一个一个题目慢慢理解。
class Solution {
List<List<Integer>> res=new LinkedList<>();//保存结果
List<Integer> path=new ArrayList<>();//保存每次的路径
public List<List<Integer>> combinationSum(int[] candidates, int target) {
backtracking(candidates,target,0,0);//回溯,sum是当前总和,start是当前开始位置
return res;
}
public void backtracking(int[] candidates,int target,int sum,int start){
if(sum>target)//如果sum大于了target直接返回
return;
if(sum==target)//如果等于,就是一种情况,保存并返回
{
res.add(new ArrayList(path));
return;
}
for(int i=start;i<candidates.length;i++){
path.add(candidates[i]);
sum+=candidates[i];
backtracking(candidates,target,sum,i);//从i开始,因为可以有重复的数
path.remove(path.size()-1);//回溯,移除路径的最后一个元素,并减去他的值
sum-=candidates[i];
}
}
}
和组数总和1基本换汤不换药,但是还是有几个细节问题需要处理。
第一:为什么需要排序,因为需要去重,把相同的元素放一起利于去重,例如
1 1 2 5 6 7 10 traget=8
这个数组中,在第一层1中已经有了1,2,5这个答案,在第二个1就不需要这个答案,而且第一个1的一层有1,1,6这个答案,所以不用担心第二层的1需要第一层的1。
第二:if(i>0&&candidates[i]==candidates[i-1]&&!used[i-1])
为什么used为false代表前一层访问过,因为回溯,第一层使用时是标志为true,但当他使用完后,被remove,同时标志为false,使用false代表使用过。
第三:i=start+1,这个很简单,因为元素不能重复使用
class Solution {
List<Integer> path=new ArrayList<>();
List<List<Integer>> res=new ArrayList<>();
boolean[] used;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
used=new boolean[candidates.length];
backtracking(candidates,target,0,0);
return res;
}
public void backtracking(int[] candidates,int target,int sum,int start){
if(sum>target) return;
if(sum==target){
res.add(new ArrayList<>(path));
return;
}
for(int i=start;i<candidates.length;i++){
if(i>0&&candidates[i]==candidates[i-1]&&!used[i-1])
continue;
used[i]=true;
path.add(candidates[i]);
backtracking(candidates,target,sum+candidates[i],i+1);
used[i]=false;
path.remove(path.size()-1);
}
}
}
和第一个更像,基本就是在一个固定数组找固定大小的组合,这里说一下为什么
backtracking(k,n,i+1,sum+i);等价于sum+=i;backtracking(k,n,i+1,sum);sum-=i;
因为sum+i只是把值传到下一层,这一层的值没变,就等价于加完再传值再回溯
class Solution {
List<Integer> path=new ArrayList<>();
List<List<Integer>> res=new ArrayList<>();
public List<List<Integer>> combinationSum3(int k, int n) {
backtracking(k,n,1,0);
return res;
}
public void backtracking(int k,int n,int start,int sum){
if(sum>n||path.size()>k) return;
if(sum==n&&path.size()==k){
res.add(new ArrayList<>(path));
return;
}
for(int i=start;i<=9;i++){
path.add(i);
backtracking(k,n,i+1,sum+i);
path.remove(path.size()-1);
}
}
}