在深度优先算法的总结中,就用到了回溯算法求解。回溯算法本身除了指导沃算法设计以外,在实际软件开发场景中应用也十分广泛。例如正则表达式的匹配,编译原理中的语法分析等等。
如何理解回溯算法
如同走迷宫一样,我们会不断地遇到分叉路口,此时可以随机选择一条路走下去。如果,后续发现这条路走不通,那么我们就返回到分叉路口的选择处,重新选择一条新的路继续走下去,直到走出迷宫。
回溯的处理思想,类似于枚举搜索。我们枚举出所有的解,然后选择其中最优的解。为了有规律的枚举出所有可能的解,避免遗漏和重复,我们把问题的求解分解成多个阶段,每个阶段随机选择一种解法,如果不能求解,就返回到上个阶段选择之初,重现选择另外一种解法。
回溯应用举例
0-1 背包问题
0-1背包问题中,0 与 1 代表的就是选择与否。
假如我们有一个背包,背包的承载重量为 Wkg。现在有 n 个物品,每个物品质量不等,并且物品不可分割。我们期望选出部分物品,使得这些物品质量之和不超过背包的承载重量,并且总质量最大。
在这个过程中,每个物品都面临两种选择:选中与不选中。所以,我们把 n 个物品分成 n 个阶段的选择,每个阶段可以选择放入该物品或者不放入该物品。我们用递归的方法处理每一个阶段。
1 | // 存储背包中物品总重量的最大值 |
上述代码有详细的注释,足以理解整个过程。需要注意的是,f 方法中抛开递归终止的条件判断以外,后续的逻辑分别执行了物品不放入背包和放入背包的两种情况。
也就是说,算法的是想依然是回溯思想,但是执行的逻辑确是一步到底,分情况执行了所有的选择。所以,我们看到的是代码直接走到最后,没有看到有返回重新选择的步骤。
正则表达式
为了理解方便,该例子中的正则表达式只有两种通配符: * 代表匹配任意多个(大于等于 0 个)任意字符, ? 匹配零个或者一个任意字符。
匹配过程中,如果遇到通配符,意味着我们到了分叉路口,有了多种选择。此时我们先任意选择一种匹配方案,然后继续匹配后续的字符。
1 | public class Pattern { |
上述代码有详细的注释,足以理解整个过程。需要特别注意的是,当遇到通配符 * 的时候,代表后续可以匹配任意个字符。也就是说,从该字符到文本结束,任意一段的字符串都可以匹配上,也代表了该分叉路口有许多的路可供选择。
总结
本文创作灵感来源于 极客时间 王争老师的《数据结构与算法之美》课程,通过课后反思以及借鉴各位学友的发言总结,现整理出自己的知识架构,以便日后温故知新,查漏补缺。
初入算法学习,必是步履蹒跚,一路磕磕绊绊跌跌撞撞。看不懂别慌,也别忙着总结,先读五遍文章先,无他,唯手熟尔~
与诸君共勉