理解回溯算法

什么是回溯算法

我们先来看回溯算法的定义(来自 维基百科):

回溯法采用试错的思想,它尝试分步的去解决一个问题。在分步解决问题的过程中,当它通过尝试发现现有的分步答案不能得到有效的正确的解答的时候,它将取消上一步甚至是上几步的计算,再通过其它的可能的分步解答再次尝试寻找问题的答案。回溯法通常用最简单的递归方法来实现,在反复重复上述的步骤后可能出现两种情况:

  • 找到一个可能存在的正确的答案;
  • 在尝试了所有可能的分步方法后宣告该问题没有答案;

深度优先遍历有着密不可分的关系,深度优先遍历的定义如下(来自 维基百科)。

深度优先搜索算法(Depth-First-Search,DFS)是一种用于遍历或搜索树或图的算法。这个算法会尽可能深的搜索树的分支。当节点 v 的所在边都己被探寻过,搜索将回溯到发现节点 v 的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。

回溯算法与深度优先遍历密不可分

简单归纳一下:回溯算法也叫 回溯搜索算法,「搜索」的意思是「搜索所有的解」。回溯算法从初始状态出发,采用 深度优先遍历 的方式,得到问题的 所有 的解。因为采用遍历的方式,所以可以得到所有的解。

在一些教程上,回溯算法也叫做 暴力搜索(也称为穷举),但它不像我们写几个 for 循环那么朴素、暴力。回溯算法按照一定的方向进行搜索,这里方向是深度优先遍历的方向。

回溯算法的适用范围

回溯搜索问题通常问我们一个问题的所有解决方案。如果解决一个问题有多个解决方案,每一个解决方案有多个步骤,题目要求我们得到所有的解,就可以使用回溯算法。

多个解决方案,每一个解决方案有多个步骤,通常可以建模成一个 树形问题。而树形问题中有着很明显的递归结构,因此 回溯算法递归地建立了局部的可能的解决方案,当发现一个可能的解决方案无法得出正确的结果时,回退到上一步,尝试下一个可能的解决方案 ,这里的 「回退」就是「回溯」的意思。

还可以这样理解 树形问题:要解决一个问题有多个步骤,每一个步骤有多种选择,使用回溯算法通过深度优先遍历得到所有的解决方案。

回溯算法有 通用解题方法 的美称,但是它采用遍历的思想,复杂度很高。我们在解决算法问题的时候,需要根据不同的问题场景设计最合适的算法。

通过全排列问题理解回溯算法是树形问题上的深度优先遍历

理解回溯算法_第1张图片

细节 5:为什么不是广度优先遍历

相比较于深度优先遍历而言,深度优先遍历在不同阶段,状态变量的差异只有 11 个操作,不像广度优先遍历那样有跳跃的行为,因此深度优先遍历可以成为强大的搜索算法。

下面我们给出一个等式,这个等式涵盖了回溯算法的内容。可以用于帮助理解回溯算法。

剪枝

什么是剪枝?

回溯算法其实是一个遍历的算法,通过遍历搜索所有的解 其实是没有技巧的,并且时间复杂度很高。因此在遍历的时候,如果能够提前知道 即将要遍历分支 不能搜索到符合条件的结果,这一分支就可以跳过,这一步操作就像是在一棵树上剪去一个枝叶,因此称为 剪枝。

剪枝有以下几点注意事项:

正确性:不能把正确的结果剪掉;
准确性:尽可能多地剪掉一些枝叶,以显著提高程序运行的效率;
高效性:剪枝也会带来一些计算的消耗,有可能会造成得不偿失。一般而言,为了剪枝做一些预处理是必要的。但工程实践中,更可靠的方法是:通过实验决定是否需要剪枝,剪枝剪到什么程度。

总结:回溯算法、深度优先遍历、递归、栈

  • 回溯算法适用深度优先遍历的方式得到一个问题的所有的解;
  • 当我们将问题转化为树形问题以后,就会发现 深度优先遍历递归地构建了局部的解决方案;
  • 深度优先遍历和递归都符合 后进先出 的规律,因此底层支持它们实现的都是 栈;
  • 回溯 指给 索一个 可能的 解决方案而无法得出正确的结果时,放弃这一次的选择,而退回到 递归树 的更浅层,调用 方法栈 的更深层继续进行求解时,引用类型参数 需要重置的行为。

 

你可能感兴趣的:(算法,数据结构,动态规划)