求最短路径问题
迪杰斯特拉算法
迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个结点到其他结点的最短路径。它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。
- 设置出发顶点为 v,顶点集合 V{v1,v2,vi…},v 到 V 中各顶点的距离构成距离集合 Dis,Dis{d1,d2,di…},Dis集合记录着 v 到图中各顶点的距离(到自身可以看作 0,v 到 vi 距离对应为 di)
- 从 Dis 中选择值最小的 di 并移出 Dis 集合,同时移出 V 集合中对应的顶点 vi,此时的 v 到 vi 即为最短路径
- 更新 Dis 集合,更新规则为:比较 v 到 V 集合中顶点的距离值,与 v 通过 vi 到 V 集合中顶点的距离值,保留值较小的一个(同时也应该更新顶点的前驱节点为 vi,表明是通过 vi 到达的)
- 重复执行两步骤,直到最短路径顶点为目标顶点即可结束
图解过程
第一步,从起始点g开始,访问g的所以相邻顶点,g设为已访问
一步过后,距离数组更新,选择距离最小的A,把A当作顶点,访问A的所有相邻未访问顶点,把A设为已访问。
由于gab这条路是7大于了gb3,就保留gb这条路。接下来在没访问过的节点里找路径最短的,发现是e,把e当作顶点,访问e的所有相邻未访问顶点,把e设为已访问。
两条路都不是最短的,都不保留,在没访问过的节点里找路径最短的,发现是b,把b当作顶点,访问b的所有相邻未访问顶点,把b设为已访问。
在没访问过的节点里找路径最短的,发现是F,把F当作顶点,访问F的所有相邻未访问顶点,把F设为已访问。
GFD比GBD短,更新为最短,在没访问过的节点里找路径最短的,发现是C,把C当作顶点,访问C的所有相邻未访问顶点,把C设为已访问。由于C的相邻节点都已经访问,就寻找下一个最短路径节点D,D的相邻节点也都访问,这时候每个节点都被访问过,退出程序,得到结果。
简单来说
迪杰斯特拉算法求最短路径的大概思路是,从第一个点开始,找它的所有相邻点(广度优先思想),记录累计路径值(第一次就是他们自己),找到这些路径值里最小的那个点(A),让它作为第二个点,从它开始,找到它的所有相邻点(当然可能有重复的),记录累计路径值(累计了第一个点(G)到第二个点(A)的路径值),找出并保留对每个相邻点最小的累计路径值,然后找到这些累计路径值里最小的那个点,让它作为第三个点,继续重复,直到每个点都作为过顶点(作为过顶点就认为它被访问过)。
代码
编写算法辅助类,该类由三个数组组成,
visit存放图的全部顶点,用来记录各个顶点的访问情况,0表示未访问1表示已访问,初始化时其他顶点都为0,传入的起始顶点为1表示已访问;
dis,长度和顶点个数相同,存放起始顶点到对应顶点的累计距离,初始化时其他位是一个极大数,代表路径暂时不通,起始顶点对应位数是0,代表自己到自己是0,对应上图每条线的蓝色数字和;
pre,存放各顶点的前驱顶点,用于记录最短路径,初始化时每位为-1,代表无前驱,对应上图每条线的蓝色字母,有几个顶点,就有几条路径,但是不必从头到尾记录一条路径,因为如上图,路径像树一样,只要记录直接前驱,就可以顺着一直找到根,因此用一个一维数组记录每一个顶点的直接前驱下标,然后到对应下标位置在找它的前驱,一直找到-1表示根的位置,就可以还原出一条路径。
辅助类需要具有的方法:
- 对三个数组初始化的方法,需要获得起始顶点下标,顶点个数
- 判断某顶点是否被访问
- 更新起始顶点到某顶点的距离为新值
- 获得起始顶点到某顶点的累计距离
- 更新某顶点的前驱顶点为某值
- 找到应该访问的下一个顶点。规则是,查找dis距离数组,找到未被访问过且距离最小的顶点,它就为应该访问的下一个顶点,把它设置成已访问。
- 输出三个数组,并输出每一条最短路径
//已访问顶点集合,用于帮助完成算法,辅助类
class VisitedVertex{
int[] visit;//存放顶点,0未访问1已访问
int[] dis;//存放出发顶点到各顶点的累计距离
int[] pre;//存放各顶点的前驱顶点
/**
* 初始化方法,对三个数组初始化
* @param index 代表起始顶点
* @param num 顶点个数
*/
public VisitedVertex(