本文仅供个人记录和复习,不用于其他用途
什么是图
简单来说,就是一些小圆点和连接他们的线组成,上一章最后一张图片显示的就是图。我们已经通过这张图片,学习到了深度优先和广度优先的不同之处。但是有一个问题,我们为什么要创建图这个概念?或者说它能够解决什么样的问题?
图的深度优先遍历
小明想去旅游,可是他并不知道应该怎么去。看看下面的地图,你能够帮助小明找出最短的路线吗?
首先我们能够确定,这是一个有向图。例如,1可以直接到2,但是2不能够反过来到1,只能通过其他的路线到达。至于路线上面的数字,就是代表着这个路线的长度。那么,这些数据应该如何存储呢?
事实上一个二维数组就能够存储。比如第一行,就是代表着1号城市到达其他城市的距离。注意,这里是直接到达,而不是通过其他的线路到达。我们规定所有城市到自己的距离为0,不能直接到达的城市距离为无穷大,可以直接到达的就写上相应的数据。
接下来我们要寻找从1号城市到5号城市的最短路径。这里我们使用深度优先算法,按照自然数的顺序,可以得到一下三条路径:
- 1-2-3-4-5,路径长为14
- 1-2-5,路径长为9
- 1-5,路径长为10
很显然,1-2-5
就是最短路径。关于代码的实现,有几个需要注意的地方:
- 用一个变量
min
来记录每次找到的最小值 - 用
book
数组标记哪些城市已经搜索过,避免死循环 - 前一步探索完毕后,记得取消标记,为下一次探索做准备
|
|
这里简要的解释一下dfs()
。首先,得判断目前的路径长有没有超过最短路径,如果超过,剩下的探索没有意义,直接返回。如果到达了目标城市,就判断目前的路径是不是更短,是的话就将min
值替换。再往下,就是深度优先中比较重要的循环。对于当前的节点,我们往下探索的条件有两个:
- 必须能够直接到达
- 之前没有走过
假如满足以上两个条件,就往下探索那个节点,否则就换下一个。那么呢,我们就将可以被探索的节点标记,对这个节点也进行深度探索,在探索完这个节点所有的可能后,取消标记。
图的广度优先遍历
说完深度,自然要说说广度。当然,有向图我就不提了,大家可以根据前一章的知识自行编写代码。这里我们来学习如何用广度优先算法来遍历无向图。
比如上面这个就是无向图,1表示起点城市,5表示目标城市,假设所有边的长度是一样的,那么最短的路径是多少?
这里存储的方式还是二维数组(一般被称作邻接矩阵),然后按照自然数的顺序,将城市依次入队。同样的,能够直接到达的城市才有资格入队。那么,当队列进行到5号城市时,探索完毕,得到最短路径。
可能有的人会问,为什么到达5号城市就是最短路径。上一章我们已经讲过它们的区别,那就是深度优先搜索只会探索纵深,你得到的答案并不一定就是最短的。但是呢,广度优先搜索每一次入队都是同一深度的点入队,当这一深度探索完了之后,才会轮到下一深度。因此,只要到达5号城市,那么这个路径一定就是最短的。
|
|
可以输入下面这组数据进行验证,结果为2则正确:
5 7 1 5
1 2
1 3
2 3
2 4
3 4
3 5
4 5
注意,当一个点扩展完后,一定要head++
让队首出队。
选择哪种算法
相比较而言,广度优先搜索更加适用于所有的边距离相同的情况。对于这个问题,广度优先搜索会更快一些。