BFS及其变形

文章目录

  • 【广度优先搜索】
    • 1.Flood Fill
    • 2.最少步数
    • 3.状态图搜索
  • 【广搜变形】
    • 1.双端队列BFS
    • 2.优先队列BFS
    • 3.双向BFS
  • 【例题】

【广度优先搜索】

借助一个队列来实现广度优先搜索,起初,队列中仅包含起始状态。在广度优先搜索的过程中,我们不断从队头取出状态,对于该状态面临的所有分支,把沿着每条分支到达的下一个状态(如果尚未访问过或者能够被更新成更优的解)插入队尾。重复执行上述过程直到队列为空。

BFS及其变形_第1张图片

对应的队列的情况:

1.Flood Fill

Flood Fill 算法是计算机图形学和数字图像处理的一个填充算法。就好像在起点倒水,看能覆盖多大的一片区域。其实就是从一点开始向四面周围寻找点填充遍历,原理和BFS很相似,当然也可以像DFS一样的遍历。

BFS及其变形_第2张图片

2.最少步数

典型的如“走地图”类问题,也就是形如“给定一个矩形地图,控制一个物体在地图中按要求移动,求最少步数”的问题。

广度优先搜索很擅长解决这种问题。地图的整体形态是固定不变的,只有少数个体或特征随着每一步操作发生改变。我们只需要把这些变化的部分提取为状态,把起始状态加入队列,使用广度优先搜索不断取出队头状态,沿着分支扩展、入队即可。

广度优先搜索是逐层遍历搜索树的算法,所有状态按照入队的先后顺序具有层次单调性(也就是步数单调性)。如果每一次扩展恰好对应一步,那么当一个状态第一次被访问(入队)时,就得到了从起始状态到达该状态的最少步数。

3.状态图搜索

BFS搜索处理的对象不仅可以是一个数,还可以是一种状态图。

典型的如八数码问题,此时就要考虑:

  1. 状态怎么表示(存储)?
  2. 状态怎么判重?
  3. 状态之间怎么转移?

【广搜变形】

1.双端队列BFS

如上分析,在最基本的广度优先搜索中,每次沿着分支的扩展都记为“一步”,我们通过逐层搜索,解决了求从起始状态到每个状态的最少步数的问题。这其实等价于在一张边权均为 1 的图上执行广度优先遍历,求出每个点相对于起点的最短距离(层次)。队列中的状态的层数满足两段性和单调性,从而我们可以知道,每个状态在第一次被访问并入队时,计算出的步数即为所求。

然而,如果图上的边权不全是 1 呢?

我们不妨先来讨论图上的边权要么是 1、要么是 0 的情况。

边权要么是 0、要么是 1 的无向图上,我们可以通过双端队列广搜来计算。算法的整体框架与一般的广搜类似,只是在每个节点上沿分支扩展时稍作改变。如果这条分支是边权为0 的边,就把沿该分支到达的新节点从队头入队;如果这条分支是边权为 1 的边,就像一般的广搜一样从队尾入队。这样一来,我们就仍然能保证,任意时刻广搜队列中的节点对应的距离值都具有“两段性”和“单调性”,每个节点第一次被访问(入队)时,就能得到从左上角到该节点的最短距离。

2.优先队列BFS

对于更加具有普适性的情况,也就是每次扩展都有各自不同的代价时,求出起始状态到每个状态的最小代价,就相当于在一张带权图上求出从起点到每个节点的最短路。此时,我们有两个解决方案:

方法一:仍然使用一般的广搜,采用一般的队列。

  这时我们不再能保证每个状态第一次入队时就能得到最小代价,所以只能允许一个状态被多次更新、多次进出队列。我们不断执行搜索,直到队列为空。

  整个广搜算法对搜索树进行了重复遍历与更新,直至“收敛”到最优解,其实也就是“迭代”的思想。最坏情况下,该算法的时间复杂度会从一般广搜的 O ( N ) O(N) O(N) 增长到 O ( N 2 ) O(N^2) O(N2)。对应在最短路问题中,就是SPFA算法

方法二:改用优先队列进行广搜。

  这里的“优先队列”就相当于一个二叉堆。我们可以每次从队列中取出当前代价最小的状态进行扩展(该状态一定已经是最优的,因为队列中其他状态的当前代价都不小于它,所以以后就不可能再更新它了),沿着各条分支把到达的新状态加入优先队列。不断执行搜索,直到队列为空。

  在优先队列BFS中,每个状态也会被多次更新、多次进出队列,一个状态也可能以不同的代价在队列中同时存在。不过,当每个状态第一次从队列中被取出时,就得到了从起始状态到该状态的最小代价。之后若再被取出,则可以直接忽略,不进行扩展。所以,优先队列BFS中每个状态只扩展一次,时间复杂度只多了维护二叉堆的代价。若一般广搜复杂度为 O ( N ) O(N) O(N), 则优先队列BFS的复杂度为 O ( N l o g N ) O(N logN) O(NlogN)。对应在最短路问题中,就是堆优化的Djjkstra算法

3.双向BFS

因为BFS本身就是逐层搜索的算法,所以双向BFS的实现更加自然、简便。

以普通的求最少步数的双向BFS为例,我们只需从起始状态、目标状态分别开始,两边轮流进行,每次各扩展一整层。当两边各自有
一个状态在记录数组中发生重复时,就说明这两个搜索过程相遇了,可以合并得出起点到终点的最少步数。

【例题】

  • AcWing 844. 走迷宫(入门):点击这里

  • AcWing 1097. 池塘计数(Flood Fill):点击这里

  • AcWing 1098. 城堡问题(Flood Fill):点击这里

  • AcWing 1106. 山峰和山谷(Flood Fill):点击这里

  • AcWing 173. 矩阵距离(多源BFS):点击这里

  • AcWing 1076. 迷宫问题(最少步数、输出路径):点击这里

  • AcWing 188. 武士风度的牛(最少步数):点击这里

  • POJ 3278 Catch That Cow(最少步数):点击这里

  • AcWing 845. 八数码(状态图搜索、BFS):点击这里

  • AcWing 1107. 魔板(状态图搜索、输出字典序最小路径):点击这里

  • AcWing 175. 电路维修(双端队列BFS):点击这里

你可能感兴趣的:(BFS及其变形)