一、概念

回溯的处理思想,有点类似枚举搜索。我们枚举所有的解,找到满足期望的解。为了有规律地枚举所有可能的解,避免遗漏和重复,我们把问题求解的过程分为多个阶段。每个阶段,我们都会面对一个岔路口,我们先随意选一条路走,当发现这条路走不通的时候(不符合期望的解),就回退到上一个岔路口,另选一种走法继续走,直到遍历完所有的走法或获取到想要的结果。
 

二、应用

1、八皇后问题

八皇后问题(英文:Eight queens),是由国际西洋棋棋手马克斯·贝瑟尔于1848年提出的问题,是回溯算法的典型案例。问题表述为:在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
 
js 代码实现:
辅助理解:假设已经填满第7行的Q,到第8行的时候,如果可以放下一个Q,那么打印这个解。如果不行,那么这个函数call8queens(8)这个函数结束,此时就回到第7行 call8queens(7),再寻找下一个适合放入Q的位置(上一次放入Q的位置开始),如果找不到,在回到上行,在寻找这一行中适合放入Q的位置;如果能找到,最终也会在第8行的找到一个位置放下Q。不断的循环调用,不断的回归,直到每一行的for循环都结束,所有的解都打印完。
 
 

2、0-1 背包问题

问题描述:对于一组不同重量、不可分割的物品,我们需要选择一些装入背包,在满足背包最大重量限制的前提下,求背包中可容纳的物品总重量的最大值。为什么叫0-1 背包问题呢,因为对于每一个物品,只有装(1)和不装(0)两种选择。
 
例如:假设现在有5个物品,各个物品的重量分别为2、3、4、5、6,背包能容纳的最大重量为16,那么我们在总重量为16 的前提下,能装下的最大组合为【2、3、5、6】。
对于0-1背包问题,我们可以采取回溯算法的方法,穷举搜索所有可能的装法,然后找出满足条件的最大值。不过,回溯算法的复杂度比较高,是指数级别的。那有没有什么规律,可以有效降低时间复杂度呢?我们一起来看看:
 

我们假设背包的最大承载重量是 9。我们有 5 个不同的物品,每个物品的重量分别是 2,2,4,6,3。如果我们把这个例子的回溯求解过程,用递归树画出来,就是下面这个样子:
 

我们发现,像 f(2, 2) 这种情况,会出现两次,并且越是到了后面,重复的情况越多,为了避免重复计算,
我们可以改良一下:
 

 

辅助理解:对于每个物品来说,都有两种选择,装进背包或者不装进背包。对于 n 个物品来说,总的装法就有 2^n 种,去掉总重量超过 Wkg 的,从剩下的装法中选择总重量最接近 Wkg 的。不过,我们如何才能不重复地穷举出这 2^n 种装法呢?这里就可以用回溯的方法。我们可以把物品依次排列,整个问题就分解为了 n 个阶段,每个阶段对应一个物品怎么选择。先对第一个物品进行处理,选择装进去或者不装进去,然后再递归地处理剩下的物品。

 

3、正则表达式的匹配

我假设正则表达式中只包含 “*” 和  “?” 这两种通配符,并且对这两个通配符的语义稍微做些改变,其中,“*” 匹配任意多个(大于等于 0 个)任意字符,“?” 匹配零个或者一个任意字符。
 
解读:我们可以依次考察正则表达式中的每个字符,当是非通配符时,我们就直接跟文本的字符进行匹配,如果相同,则继续往下处理;如果不同,则回溯。如果遇到特殊字符的时候,我们就有多种处理方式了,也就是所谓的岔路口,比如“*”有多种匹配方案,可以匹配任意个文本串中的字符,我们就先随意的选择一种匹配方案,然后继续考察剩下的字符。如果中途发现无法继续匹配下去了,我们就回到这个岔路口,重新选择一种匹配方案,然后再继续匹配剩下的字符。