回溯法模板
void backtracking(参数) {
// 子集问题直接存放结果,不需要终止条件判断
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
// 这里一般两种处理方法,
// 一是直接收集结果,
// 二是先进行if判断,这个判断多种多样,有时是判断是否符合某条件,有时是去重,
// 根据判断,收集结果或者continue跳过。
backtracking(路径,选择列表); // 递归,上图黑色箭头
回溯,撤销处理结果 // 上图红色箭头
}
}
回溯法去重
这里解释一下去重的意思。要知道一个元素在一个path中只能取一次,去重是在有重复元素(两个元素值相同)的情况下,为了防止出现两个重复的path,进行的去重操作。
所以,要去重的是同一树层上的“使用过”,同一树枝上的都是一个path集合里的元素,不用去重。
树层去重,需要对数组先排序!特殊情况除外(目前只遇到递增子序列问题不能排序)
去重方法
- 排序 + 使用set
在每个树层新建一个set(也就是每个for循环开始前定义set),处理节点前先判断set中是否包含该节点值。如果已包含,说明树层重复,continue;不包含则把该值加入到set,继续处理。
- 排序 + 使用used数组
推荐在排列问题使用,used数组可以一举两用。
// used[i - 1] == true,说明同一树枝candidates[i - 1]使用过
// used[i - 1] == false,说明同一树层candidates[i - 1]使用过
// 要对同一树层使用过的元素进行跳过
if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) {
continue;
}
排列问题下使用used数组去重示例 力扣47.全排列Ⅱ
class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
boolean[] used;
public List<List<Integer>> permuteUnique(int[] nums) {
used = new boolean[nums.length];
Arrays.sort(nums);
back(nums);
return res;
}
void back(int[] nums){
if(path.size() == nums.length){
res.add(new ArrayList(path));
return;
}
for(int i = 0; i < nums.length; i++){
if(i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false){ // 这里树层去重
continue;
}
if(used[i] == false){ // 排列问题,用used数组保证一个path里一个元素只能使用一次
used[i] = true;
path.add(nums[i]);
back(nums);
used[i] = false;
path.remove(path.size() - 1);
}
}
}
}
小tips: used数组定义为全局变量boolean[] used;,再在主要函数中初始化used = new boolean[nums.length];,这样在回溯函数中可以不传入used数组参数。
- 排序 + 使用startIndex
推荐在组合或子集这种需要使用startIndex的问题使用。
if (i > startIndex && candidates[i] == candidates[i-1]) continue;
排列问题
- 排列问题和组合问题、切割问题、子集问题最大的不同就是for循环里不用startIndex了,因为排列问题,每次都要从头开始搜索(for循环i从0开始)。
- 排列问题需要记录path里都放了哪些元素了(因为每次for循环i从0开始,需要防止一个元素在一个path中取多次),可以使用used数组,在java中也可以使用
if(path.contains(nums[i])) continue;做判断的方式。
组合问题,什么时候需要startIndex
- 如果是一个集合来求组合的话,就需要startIndex,例如:77.组合 (opens new window),216.组合总和III (opens new window)。
- 如果是多个集合取组合,各个集合之间相互不影响,那么就不用startIndex,例如:17.电话号码的字母组合