外在
显然,给你一个选择列表,让你在选择列表中不断的进行选择。最终返回一个包含所有选择的结果集
结果集 fun(选择列表 nums) {
}
构造基本解题结构
结果集都是从从路径一步步走出来的,backtrack多出了一个路径参数,无需返回。
结果集 result;
void backtrack(选择列表 chooses, 路径 track){
}
结果集 fun(选择列表 chooses) {
// 路径是结果的子集,一步步扩充成结果
路径 track;
backtrack(chooses, track);
return res;
}
构造回溯结构
回溯是一个递归结构,带结束条件,调用自身。
每次选择是可多选的,故以遍历选择列表的方式逐个尝试。
由于尝试时,将选择添加进了路径中,故下次选择前,要回退上次的选择。
选择不合法时咋办?两种做法:在开头排除,在遍历尝试时不管;认为调用传进来的必然合法,调用前先完成排除。
void backtrack(选择列表 chooses, 路径 track) {
if (触发结束条件) {
result.add(track);
// 如果没有return,则可能沿着剩余选择再搞搞,说不定会死循环
return;
}
// 遍历选择列表
for (choose:chooses) {
if (选择不合法)
continue;
// 做选择,路径中加入当前选择
track.add(choose);
// 进入下一层决策树
backtrack(nums, track);
// 回退选择,还原原本的track再进入下个操作
track.remove(choose);
}
}
选择和回退是一对的,是为了不影响数据。此外,把路径copy一个新的也阔以啊,就是效率低。
回溯算法只返回其中一个结果
boolean backtrack(选择列表 chooses, 路径 track) {
if (触发结束条件) {
result.add(track);
// 如果没有return,则可能沿着剩余选择再搞搞,说不定会死循环
return true;
}
// 遍历选择列表
for (choose:chooses) {
if (选择不合法)
continue;
// 做选择,路径中加入当前选择
track.add(choose);
// 进入下一层决策树
if(backtrack(nums, track)){
return true;
}
// 回退选择,还原原本的track再进入下个操作
track.remove(choose);
}
return false;
}
再次简化的话,可以去掉result这个量
与动态规划的关系
能采用动态规划的解决的问题,也是符合回溯算法特征的,动态规划的优势在于重叠子集上,做了更大幅度的剪枝。