一、前言

很久之前在做题的时候,隐隐发现“搜索”这种策略的影子,原打算做一个总结来着,但后来因为没有一个较为系统的学习,所以就搁置了。这段时间在看《挑战程序设计》这本书的时候,才发现这确实是一种解题策略,叫做“最基础的穷竭搜索”。
基础搜索小讲一二三

搜索本质上是一种从所有的状态空间里寻找可行解(或者最优解)的过程,根据搜索的策略不同,常见的搜索方式有以下几种:

  • 深度优先遍历
  • 广度优先遍历
  • 特殊状态空间的枚举

以上是一些基本的搜索方式,还有一些更高级的搜索方式。如

  • A*
  • IDA*

小弟才疏学浅,目前只看了关于基础篇的搜索,高级篇的搜索被一题《Square Destroyer》 卡住了许久(且面试的时候大都考不到高级的搜索),这儿只总结关于基础部分的搜索方式。

二、深度遍历

深度优先遍历说起来也很简单,就是不断往“深”处搜索,直到无法继续深挖下去,则返回上一层,换下一个结点,重复以上步骤。最终达到搜索所有状态空间的目的。
基础搜索小讲一二三
如上图所示,红色为前进路线,蓝色为返回路线。 从节点0开始。(上图只花了一部分)

深度遍历相对来说使用的比较多,因为其可以利用递归这个利器,使得代码看起来相当简洁(前提条件是你能写好递归的式子)。

这里还需要注意一点,上面的示意图比较简单,

三、广度遍历

广度遍历其实也很简单,就是优先从当前状态往四周扩展,就像一条条章鱼的触手一样。当四周的状态都访问完毕(需要加入到队列里)之后,在从队列取出下一个状态,继续以上步骤。如下图所示。

基础搜索小讲一二三

如上图所示,从状态0开始,红色为从状态0第一次访问的状态,蓝色为从状态1(需要从队列里取出来)开始扩展的状态。(上图只画了一部分)

广度遍历和深度遍历一样,也可以访问到所有的状态空间,但是相对来说用的比深度遍历要少一些,因为其使用一般需要自己构造“队列”这个数据结构来存储待访问的状态(深度优先遍历使用栈这个数据结构,编译器已经帮我们实现好了),相对来说麻烦一些。 当然,有些问题只适合使用深度遍历的方式。

但是广度遍历有一个特点,其访问的顺序是从里往外的,或者说从源节点往四周扩展的,所以其状态是按照距离初始状态由近到远的顺序遍历的。按照这个特点,我们可以求最短路径(当然,这个条件需要所有的路段距离都是一样的)

四、特殊状态的枚举

还有一种最直接最简单的方式,就是直接列举所有的状态空间( 当然,这需要状态空间或者题目比较特殊时才可以)。 我们可以利用位运算,枚举从n个元素中取出k个元素组合、排列或者其他一些方式,如按照二项式选取(进行可行解的搜索。

比如说,{1,2,3,4,5}, 我们可以按照是否选取每个元素,划分出2^5 = 32中状态空间。

基础搜索小讲一二三
如上图所示,每个元素都有被选取或者不被选取的可能。

五、总结

好了,文章的最后,来总结一波。
(1)、上文所说的所有的状态空间,有的并不是向上面介绍的那样简单,就是一个单独的数或者结点。 状态空间中的状态可能包括实际好多个结点,甚至是有一定顺序规则的结点。

(2)、正因为上面的那个特点,所以实际的问题中深度优先遍历或者广度优先遍历这些算法并不仅仅用于树或者图这种显而易见的问题中,还有可能用于数组这样表面上看是一维的数据结构上(数据是一维的,问题可能是二维的或者多维的)

(3)、leecode中深度优先遍历有一些是数组这样一维的题,也有可能是矩阵深度遍历这样二维的题。本质上其实是一样的。只不过前者可能只有两个方向(一般从前到后),后者是上下左右四个方向。
基础搜索小讲一二三

相关文章:

  • 2021-10-04
  • 2022-12-23
  • 2022-12-23
  • 2021-12-26
  • 2021-09-14
  • 2021-06-15
  • 2022-12-23
猜你喜欢
  • 2021-12-12
  • 2021-08-07
  • 2021-11-08
  • 2021-10-11
  • 2021-07-19
  • 2022-12-23
  • 2022-02-28
相关资源
相似解决方案