暴力美学2—BFS广度优先搜索(算法图解第二弹)

Posted Xu说要改变世界

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了暴力美学2—BFS广度优先搜索(算法图解第二弹)相关的知识,希望对你有一定的参考价值。

强烈推荐初学者看《算法图解》这本书,


这本书真的是浅显易懂,而且非常照顾我这种水平的小菜狗,


对新人十分友善。


我对算法的学习顺序也是根据这本书展开的,


但这本书上只写了BFS广度优先搜索,没有写BFS深度优先搜索,


因此考虑到俩者的关联性,我就先去初步掌握了DFS之后再来学习BFS,


有了前面的基础,BFS就相对好掌握很多了。


DFS算法和BFS算法都是遍历图的算法,


只是它们遍历的方式有所不同,这就会导致在实际应用中它们的应用场景也会有所不同,


但其实在很多情况下俩者都是可以相互替换的。


其中这篇文章中讲的DFS算法广度优先算法的主要目的是找出最短路径(但不是带权的),


举三个《算法图解》上的例子:


1.编写国际跳棋,计算最少走多少步就可获胜;


2.编写拼写检查器,计算最少编辑多少个地方就可将错拼的单词改成正确的单词,如将READED改为READER需要编辑一个地方;


3.根据你的人际关系网络找到关系最近的医生;


我下面给出一张图,你就可以很清楚地看出BFS算法的基本思想了。



BFS算法的搜索顺序是先搜索初始节点相邻的节点,然后将这次搜索中搜索节点的相邻节点记录下来,


等这一轮搜索完后(也就是初始节点相邻的节点搜索完后),再搜索已经记录下来的节点,将这个节点的相邻节点记录下来,


等这一轮搜索完后,再搜索记录下来的节点。之后就是不断重复这个过程。 


这其实就是队列的实现,


队列的工作原理和现实生活中的队列完全相同,队列只支持入队和出队,并且是“先进先出”,“先到先走”,十分公平。


BFS算法是将记录的节点直接塞到队列后面,


等到前面的节点都搜索完,离开队列后,再开始搜索这些节点,


从这个角度看,BFS实际上就是对应队列这个数据结构,


而这个数据结构在C++中我们可以通过queue模板实现。


当然,这个算法理解起来虽然比较简单,但是想要用代码实现,还是有点难度,


这边我就举两个实际的例子。


第一个例子


Joker先生最近手头没钱了,于是开始着手挖油田(后面程连块),在暴力掠夺了一大堆土地后,他需要你给他统计每一个土地中油田的数量,由于Joker先生过于贪婪,土地简直无法度量,因此人工是无法实现的,需要你通过编程让计算机小朋友去帮你统计。要求:输入一个m行n列(1<=m,n<=100)的字符矩阵,统计字符“@”组成多少个连块。如果两个字符“@”所在的格子相邻(八个方向),就说明他们属于同一个连块。多组输入,当m=0时,输入结束。


如图,就有两个连块

暴力美学2—BFS广度优先搜索(算法图解第二弹)


这道题目我之前将其放到了DFS算法的专题里面,


实际上这道题目完全可以用BFS来做,


因为在这道题目里面,只需要实现搜索的功能就可以了,因此无论是DFS还是BFS都可以实现。


由于本人实在过于懒惰,这边就将大佬的代码贴上去了,


我主要是在后面加上了注释,帮助大家理解,


因为没有注释去阅读代码实在过于吃力。。。


暴力美学2—BFS广度优先搜索(算法图解第二弹)
暴力美学2—BFS广度优先搜索(算法图解第二弹)
暴力美学2—BFS广度优先搜索(算法图解第二弹)

可以看出,实际上BFS算法的实现相对DFS还更好理解一点,因为DFS涉及到递归的问题,


而BFS算法就是一个很简单的队列的“进入进出”。


当然,这只是因为题目相对简单,若是碰到复杂的题目,想要编出正确的BFS算法还是相当有难度的。


这边我再给一个例子。


第二个例子


Problem G: 分球

Time Limit: 1 Sec Memory Limit: 64 MB

Submit: 30 Solved: 7


Description

在一个装满财宝的屋子里,有2N个盒子排成一排,除了两个相邻的空盒外,其余的每个盒子里都装有一个金球或银球,总共有N-1个金球和N-1个银球。下面是一个N=5的列子 ,G表示金球,S表示银球。

________________________________________

| G | S | S | G | | | G | S | G | S |

-----------------------------------------

任意2个相邻的非空的盒子里的球可以移动到两个相邻的空盒中,移动不能改变这2个球的顺序。写一个程序,用最小的移动次数把所有的金球都移到所有的银球的左边。


Input

输入包含多组数据。第一行为K,表示数据的总数。每组数据的第一行是N(3 <= N <=7),第2行是2N个盒子的初始状态。金球用 a表示,银球用b,空盒用空格表示。每2组相邻数据用空行隔开。


Output

对于每一组数据,若无解则输出一行NO SOLUTION,若有解,每行输出移动的步数


Sample Input

3

3

abab


5

abba abab


6

a babbababa


Sample Output

NO SOLUTION

3

4


这道题目是期末考试的压轴题,因为当初没学过搜索算法,所以完全没办法做,


最近恰好在研究搜索算法,因此为了弥补一下自己心中的遗憾,就回头做了下这题,


确实,这道题目对现在的我而言还是比较难的。。。也是经历了一番波折才勉强将其做出来。


这道题目由于是要求最小步,并且涉及到搜索,我们可以联想到要使用BFS搜索算法,


当然DFS应该也是可以的,不过在这里有可能会超时,最好还是使用BFS算法。


思路大致分成三块:


1.判断当前状态是否符合“左金右银”

2.如何改变小球位置

3.如何对状态进行存储


第一点比较简单,主要通过编写judge函数实现,


不过这里有一个细节,就是我一开始把题目意思理解成在空格左边都是金,在空格右边都是银才符合要求,


但其实这样是错误的,只要“左金右银就可以了”,中间可以穿插空格,是我想太多了。


说到底还是我审题太糙。


第二点的话用string自带的函数就比较容易实现,就是通过substr进行拼接,有了这个函数就相对比较简单了。


第三点是这道题目的关键,我们必须要让步数和当前状态同步存储,


这里就需要用到pair模板(当然用结构实现也可以),然后将pair加到队列中就可以了,

但是,这个问题还涉及到一个最重要的环节,


就是什么时候当前搜索会无法进行下去(注意这里不要和队列为空搞混),


这是一个比较难想到的点,


其实就是当遇到重复出现的情况的时候,当前搜索就要结束了,开始下一次搜索。


而一看到去重,我们就应该可以直接联想到map模板,这个可是去重神器,


因此我们将出现过的状态存到map模板里面,就可以很好地实现去重效果。


代码如下


暴力美学2—BFS广度优先搜索(算法图解第二弹)
暴力美学2—BFS广度优先搜索(算法图解第二弹)
暴力美学2—BFS广度优先搜索(算法图解第二弹)
暴力美学2—BFS广度优先搜索(算法图解第二弹)

上述是用BFS实现的,可以看出,如果是一开始接触的话,其实是比较有难度的,


在这之后,出于好奇,我又尝试用DFS算法解了下这道题。


但很遗憾,我写出来的DFS算法带不动。。。


如果有人用DFS做出来了,可以Q我一下,这我确实没有法子。


我的失败代码如下:


暴力美学2—BFS广度优先搜索(算法图解第二弹)
暴力美学2—BFS广度优先搜索(算法图解第二弹)


以上是关于暴力美学2—BFS广度优先搜索(算法图解第二弹)的主要内容,如果未能解决你的问题,请参考以下文章

算法图解:广度优先搜索

算法图解:广度优先搜索

基础算法 --- BFS(广度优先搜索/宽度优先搜索)

算法之深度和广度优先搜索算法

《算法图解》3

Python算法-深度优先搜索&广度优先搜索(DFS&BFS)