回溯算法模板:
void backtracking(参数) {
if(终止条件) {
存放结果;
return;
}
for(选择;本层集合中元素) {
处理节点;
backtracking(路径,选择列表);
回溯,撤销处理结果;
}
}
-
组合问题
N个数里面按照一定规律找出k个数。(集合是具有无序性的)
①给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
②找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
③给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
④给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为target的组合。candidates中的每个数字在每个组合中只能使用一次。
for循环进行横向遍历,递归进行纵向遍历,回溯不断调整结果集。
最后收集叶子结点的结果
①、②解释
①和②的截止条件都为path的个数为k。区别在于②在k个数相加和为n时才放入result,①全部可以放入。
在进行for循环时,①和②都需要从startIndex开始并且for循环中的回溯传入的应为i+1(不是i),因为,数组中的每个元素不能重复使用。
还可进行剪枝优化操作:
//①
for(int i=startIndex;i<=n-(k-path.size())+1;i++)
//②
for(int i=startIndex;i<=9-(k-path.size())+1;i++)
①:
②:
③、④解释
③和④没有个数的限制,因此终止条件为和大于等于目标值,若等于目标值,放入result集合中;若不等于目标值,则不放入。
if(sum>=target) {
if(sum==target) result.push_back(path);
return;
}
③中candidates数组没有重复元素并且元素可重复利用。因此backtracking中传入的为i。
④中candidates数组有重复元素并且每个元素只能使用一次,因此在进行回溯时需要去重,该去重是对同一树层进行去重,但同一树枝不用进行去重(因为所求数组中可以出现两个相同的元素,因为candidates中可能包含数值相同但不是同一个的元素)。
可利用used数组进行树层去重(利用该方法去重需对candidates数组进行排列,确保相同数值的元素挨在一起)
sort(candidates.begin(),candidates.end());
最开始令uesd数组中的元素都为false,每次取一个元素,该位置的used变为true,进入下一层取值,跳出该层循环后该位置的uesd又变回false。因此当used为false,且下一位置元素与该位置元素相等,说明是树层间元素重复,需要去重(下一位置操作应该跳出循环),当used为true,且下一位置元素与当前位置元素重复说明是树枝元素数值重复,无需去重。
if(i>0&&candidates[i]==candidates[i-1]&&used[i-1]==false) continue;
used[i - 1] == true,说明同一树枝candidates[i - 1]使用过
used[i - 1] == false,说明同一树层candidates[i - 1]使用过
-
切割问题
一个字符串按一定规则有几种切割方式。
①给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
-
子集问题
一个N个数的集合里有多少符合条件的子集。
①给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
②给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
③给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。
在树形结构中,子集问题是收集所有节点的结果。
①、②解释:
①与②的区别在于①中没有重复元素,②中有重复元素,需要去重。
①中只要收集到path就放入result中,在for循环中,回溯传入的为i+1,因为数组中不能重复选取元素。
在②中需对树层进行去重操作,利用used数组方法进行去重,操作与组合问题相同。
③中与②的区别为③不可以进行排列,因为排列后数组顺序发生改变,不能看是否为递增序列。因此,利用unordered_set对同层元素进行去重。
if ((!path.empty() && nums[i] < path.back())|| uset.find(nums[i]) != uset.end())
{
continue;
}
-
排列问题
N个数按一定规则全排列,有几种排列方式。
①给定一个 没有重复 数字的序列,返回其所有可能的全排列。
②给定一个可包含重复数字的序列,按任意顺序返回所有不重复的全排列。
①与②的区别在于二需要去重
在①中,利用uesd数组记录使用过的元素,当前层使用过该元素过后,令used[i]=true,进入下一层后遇到true跳出循环,完成排列操作。
if(used[i]==true) continue;
②可利用used数组对其进行去重操作。
if(i>0&&nums[i]==nums[i-1]&&used[i-1]==false) continue;
if(used[i]==true) continue;