12.1-深搜(DFS)与广搜(BFS):初识问题状态空间(算法基础篇)

30 阅读5分钟

搜索的核心概念

问题求解树

问题求解树又叫问题状态树或状态求解树。这不是一个真实的计算机结构,而是我们思维逻辑层面的一种结构。如下图,我们可以将状态1展开得状态2、状态3、状态4,而状态3又展开得状态5和状态6.

          状态1
     /      |        \
  状态2    状态3      状态4
          /     \
       状态5    状态6

光看上面的状态树很难理解问题求解树的实际使用场景,接下来,咱们来举一个例子:

// 走迷宫,如下的二位数组代表一个迷宫,小狗🐶要怎样才能找到自己心爱的肉骨头🍖呢,其中🏖代表是海滩,小狗可以走这里,💣代表是有炸弹,不能走,不能走
const arr = [
  [🐶,💣,🏖,🏖,🏖,🏖],
  [🏖,🏖,🏖,💣,🏖,🏖],
  [🏖,💣,🏖,💣,🏖,🏖],
  [🏖,🏖,🏖,🏖,🏖,🍖],
];
// 我们以每个节点的坐标作为我们问题求解树的状态,初始坐标为[0,0],即小狗所在的位置,那么,小狗每走一步我们就能知道他下一步能走的状态(坐标)是什么,于是就生成了这样的一棵问题求解树。
                                             🐶
                                            [0,0]
                                              |
                                            [1,0]
                                        /           \
                                     [2,0]         [1,1]
                                       |             |
                                     [3,0]         [1,2]
                                       |             |
                                     [3,1]         [2,2]
                                       |             |
                                     [3,2]         [3,2]
                                   /       \     /        \
                                [2,2]     [3,3][3,1]     [3,3]
                                  |         |    |         |
                                 ...      [3,4] ...      [3,4]
                                         /     \       /       \
                                      [2,4]   [3,5]  [2,4]    [3,5]
                                                🍖              🍖
// 最终小狗终于找到了他想要的肉骨头了。上面的问题求解树是精简版的,没有吧所有情况都画出来,但已经能够说明问题了

从上面的示例中我们可以发现:一些与我们求解的问题息息相关的量(示例中就是坐标)构成了问题求解树的节点,分析问题的时候,最重要的一步是定义什么是这个问题的状态。需要特别注意的是,上面所说的问题求解树都是我们大脑的思维结构,而不是在程序里面的真实数据结构,因为这个这棵树理论上来说是有无限中可能的,比如小狗在[0,0]和[1,0]这两个位置来回走,那么它就陷入了“死循环”,需要走无限步才能找到肉骨头。如果上述结构是我们程序中的结构的话,大家应该很清楚,出现死循环意味着什么,那就是程序崩溃。

搜索的核心概念

1. 什么是深搜和广搜

深搜和广搜就是针对于问题求解树的不同的遍历方式,由于我们问题求解树是存在于思维逻辑层面的结构,因此这里所说的遍历也是在思维逻辑层面的遍历,不是具体的程序实现的遍历方式。

深搜(DFS:深度优先搜索)

搜索时尽量往深处搜索,比如我们经常说的知识深度,就是对某些类型的知识点达到专家的地步,不断往深层次探究,最终到达计算机底层、硬件编程、材料与物理等领域。总结一下,就是专家路线

深搜的过程其实类似于我们树形结构的递归遍历,先一条路走到黑,碰壁了,再往回走一步(回溯),如果有其他出路,就往另一条路一条路走到黑,直到碰壁(继续回溯)或到达终点(返回结果)。

总结一下:深度优先遍历通常情况下是以递归的程序形态来实现的

广搜(BFS:广度优先搜索)

适合最优化问题的求解

搜索时尽量往远处搜索,比如我们经常说的知识广度,就是不一定对某类型的知识点达到很深的理解,但掌握的知识点所横跨的领域很广,什么都会一点,知识面广博。总结一下,就是全栈路线

广搜通常需要使用层序遍历的方式进行搜索,搜索过程需要额外借助队列这样的数据结构。也就是说,当我们遍历到第一层时,先将第二次层的所有节点加入队列,然后依次遍历队列中的每一个节点,再进入下一层继续上述操作。

看到这个描述,有没有一些二次元的小伙伴想到了什么?哈哈哈,就是影分身。假如鸣人执行某些任务时,发现前面有很多分叉路,他会怎么办,有多少条岔路,就分出多少个影分身,然后同时探索每一条岔路,如果之后再出现岔路,继续影分身,直到到达正确的目标点。假如说在某一条分叉路,同时有两个影分身到达入口,此时就没必要两个影分身一起行动了,可以让其中一个融入另一个影分身,节省查克拉,这种操作就是剪枝。

影分身

图示

由于我们广搜始终是处理完上一层的所有节点之后,再处理下一层的所有节点,因此,广搜更适合处理最优化问题,即求最优解。就像上面说的,鸣人先让影分身去探路,找到最优路线。

2. 什么是搜索的剪枝和优化

通过排除某些明确不可能达到目的子树,在搜索时不搜索这些子树,以达到提升搜索效率的目的,这就是我们经常说的搜索剪枝

拿上面的问题求解树来说,假如说我们通过某些判断手段,明确最左侧的子树不可能达到目标(可能出现死循环),不搜索最左边的子树,也就是在问题求解树中将最左边的“树枝”减掉,这样就不会分摊给其他树枝提供的“营养”(搜索资源)了。

3. 设计搜索算法的核心关键点

设计问题求解树中的状态