首先看看通用模板:
这篇博客很棒一定要去看看
for 选择 in 选择列表:
# 做选择
将该选择从选择列表移除
路径.add(选择)
backtrack(路径, 选择列表)
# 撤销选择
路径.remove(选择)
接下来入手两道题目就可以掌握其规律了:
1.subset
决策树:
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
Arrays.sort(nums);
backtrack(list, new ArrayList<>(), nums, 0);
return list;
}
private void backtrack(List<List<Integer>> list , List<Integer> tempList, int [] nums, int start){
list.add(new ArrayList<>(tempList));
for(int i = start; i < nums.length; i++){
tempList.add(nums[i]);
backtrack(list, tempList, nums, i + 1);
tempList.remove(tempList.size() - 1);
}
}
2.permutation
决策树:
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
// Arrays.sort(nums); // not necessary
backtrack(list, new ArrayList<>(), nums);
return list;
}
private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums){
if(tempList.size() == nums.length){
list.add(new ArrayList<>(tempList));
} else{
for(int i = 0; i < nums.length; i++){
if(tempList.contains(nums[i])) continue; // element already exists, skip
tempList.add(nums[i]);
backtrack(list, tempList, nums);
tempList.remove(tempList.size() - 1);
}
}
}
说些规律:
1.for循环上面的代码,这部分是到达某个节点,但是还未执行此节点拥有的选择时。
2.for循环要遍历出决策树顶部拥有的所有选择
3.for循环的水平方向,路径记录templist,每次新的选择它都是空(水平方向对templist无影响)
记住这两个模型:subset和permutation!!!
subset是竖直方向有影响(竖直方向的节点,不能选择前一个节点选择过的路径)且水平方向也有影响(水平方向的节点,不能选择左边节点选择过的路径)。
permutation是竖直方向有影响,而水平方向无影响。
**
尤其是要记住subset的模型(区间模型),很多都是用它来改编的
**
接下来再让我们发现点规律:
Combination sum
class Solution {
public static List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> ans=new ArrayList<>();
backtrack(ans,candidates,0,new ArrayList(),target);
return ans;
}
public static void backtrack(List<List<Integer>> ans,int[] nums,int start,List<Integer>temp,int remain){
if(remain==0){
ans.add(new ArrayList<>(temp));
}
if(remain<0)
return;
for (int i=start;i<nums.length;i++){
temp.add(nums[i]);
backtrack(ans,nums,i,temp,remain-nums[i]);
temp.remove(temp.size()-1);
}
}
}
这个是竖直方向无关(上面节点的选择对下面的节点的选择无关),但水平方向有关系(左边节点的选择对右边节点的选择有关)。
注意看代码:与subset比较,这里竖直方向无影响,所以这里与subset不同的是,start是i而不是i+1
combinationSum2
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>>ans=new ArrayList<>();
Arrays.sort(candidates);
backtrack(candidates,new ArrayList<>(),ans,0,target);
return ans;
}
public void backtrack(int[] nums,List<Integer>temp,List<List<Integer>>ans,int start,int remain){
if (remain==0)
ans.add(new ArrayList<>(temp));
if (remain<0)
return;
for (int i=start;i<nums.length;i++){
if (i>start&&nums[i]==nums[i-1]) continue;
temp.add(nums[i]);
backtrack(nums,temp,ans,i+1,remain-nums[i]);
temp.remove(temp.size()-1);
}
}
}
这里是水平方向有关系(右边不能用左边用过了的元素),竖直方向也有关。
这里用了小技巧,把nums先排序一下,这样相同的值就只能相邻出现了,也是注意看for循环部分的改编。