搜索回溯算法是一种通过探索所有可能的候选解来找出所有解的算法。它采用深度优先搜索策略,尝试分步地去解决问题。在搜索过程中,当通过一系列的选择到达某个状态时,如果确定该状态不可能是最终解(或者不可能达到最终解),则“回溯”到上一步,尝试其他的选择。
原理:
1.候选解:从根节点(初始状态)出发,搜索解空间树。树中的每个节点代表一个候选解。
2.选择:在搜索树的当前节点,根据问题的约束条件,选择下一个扩展节点。
3.剪枝:如果确定当前候选解不可能成为最终解,则停止对该候选解所在子树的进一步搜索。
4.回溯:当从当前候选解不能得到最终解时,返回其前驱节点,并换一条路走。
5.目标测试:当达到某个节点时,判断该节点是否满足问题的目标条件,如果满足则将其加入到解集中。
通用步骤的伪代码:
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
举例:
leetcode39
解题的思路和理解,利用搜索二叉树这样一个虚拟的结构,对于原数组来说,按照顺序可以依次选择需不需要,如果需要,添加到目标数组,那么target就要减去该元素,但是要注意回溯,需要再把该元素remove了。
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> ans = new ArrayList<List<Integer>>();
List<Integer> combine = new ArrayList<Integer>();
dfs(candidates, target, ans, combine, 0);
return ans;
}
public void dfs(int[] candidates, int target, List<List<Integer>> ans, List<Integer> combine, int idx) {
if (idx == candidates.length) {
return;
}
if (target == 0) {
ans.add(new ArrayList<Integer>(combine));
return;
}
// 直接跳过
dfs(candidates, target, ans, combine, idx + 1);
// 选择当前数
if (target - candidates[idx] >= 0) {
combine.add(candidates[idx]);
dfs(candidates, target - candidates[idx], ans, combine, idx);
combine.remove(combine.size() - 1);
}
}
}