第四章:图的搜索
要研究图的搜索,首先要讲清楚什么是图;你可以理解为:图就是顶点和顶点之间连接的集合;
比如全国的铁路线网,我们将车站抽象为顶点,将连接车站的铁路抽象为车站之间的连接,如果有一套寻路算法,我们就能轻松得知从A车站到B车站怎么走最近,同时还能得知要怎么换乘;
图中顶点之间的连线是可以有权重的(比如车站之间每条铁路的长度不同),连接也可以是单向的(就像生活中的单行道);
根据搜索的策略不同,有广度优先搜索和深度优先搜索;
广度优先搜索和深度优先搜索
所谓广度优先,和深度优先相反;指的是尽可能同时涉猎尽可能多的路径,提高搜索的广度;比如搜索一个堆时,广度优先搜索会逐层遍历搜索,而深度优先搜索会一条路走到黑,每次只访问一个分支直到遇到死角,再返回最近的没有死角的上一级继续搜索;
下面我们基于这张图来详细讲讲两种搜索方式的区别:
如果使用广度优先搜索,则搜索的顺序是:
- a
- b,c,d
- e,f,h,i,j
- k,g,l
如果使用深度优先搜索,则搜索的顺序是:
- a,b,e,k
- f
- c,h,j
- d,i
- j,l
贝尔曼-福特算法
这是一个寻路算法;下面是一个闭环的图,我们将尝试使用这个算法在下面的图中进行寻路;
贝尔曼-福特算法的逻辑是:
- 每条连接两个节点的路径都有权重,节点拥有自己的“值”,表示从起点到该节点的距离;算法开始执行时,起点的值为0,其它节点全部为正无穷;节点的值会随着算法的进行不断更新;
- 某个节点的值由路径另一端的节点加上路径的权重影响;如果相加的值小于节点的值,则将节点值更新为这个值,否则不变;
- 循环执行确认节点值的操作,直到所有节点的值都不再更新为止;
算法执行完毕后我们得到了一张标注了每个节点的权重的值,从起点开始每一步都选择最小的节点作为下一步的方向,就能获得从起点到终点的最短距离;
你可能会问:现在知道最短距离了,但是我怎么确定最短路径呢?
实际上路径也是在寻路算法的运行过程中确定的;每个节点值都来自一条路径,这条最后更新了节点值的路径就是激活路径,在寻找最短路径时未激活路径将不被考虑;从终点开始,一步步选择值降低的节点,就可以回溯到起点,经过的路径也就是从起点到重点的最短路径;
设图的定点数为n,边数为m,时间复杂度是 O ( n m ) O(nm) O(nm);但这种算法的问题是:没有办法计算含有非正值权重的图;
狄克斯特拉算法
迪克斯拉特算法也在开始的时候将起点设置为0,将其它所有点设置为正无穷;但两者寻路的部分逻辑有所不同,带来的直观不同就是狄克斯特拉算法可以处理含权重0和负数路径的图;
狄克斯特拉寻路算法的逻辑是:
- 将顶点下游激活为待选择顶点,并尝试更新更新权重
- 选择权重最小的待选择顶点作为新的顶点
- 持续进行这个过程直到所有顶点都被遍历为止
接下来我们得到了一幅激活路径并计算过权重的图,像贝尔曼福特那样回溯算法,就可以找到从起点到任意终点的路径了;
迪克斯拉特寻路算法的时间复杂度是 O ( n 2 ) O(n^2) O(n2),当顶点数为n路径数为m时;
当一个图中存在负数路径时,理论上讲这个图不存在最短路径;贝尔曼-福特算法能直接发现没有最小值,但狄克斯特拉算法会计算出一个错误的结果;