上篇博客咱们详细的介绍了两种经典的最小生成树的算法,本篇博客咱们就来详细的讲一下最短路径的经典算法----迪杰斯特拉算法。首先咱们先聊一下什么是最短路径,这个仍是比较好理解的。好比我要从北京到济南,而从北京到济南有好多条道路,那么最短的那一条就是北京到济南的最短路径,也是咱们今天要求的最短路径。git
由于最短路径是基于有向图来计算的,因此咱们仍是使用上几篇关于图的博客中使用的示例。不过咱们今天博客中用到的图是有向图,因此咱们要讲上篇博客的无向图进行改造,改为有向图,而后在有向图的基础上给出最小生成树的解决方案。本篇博客咱们的思路与以前数据结构相关博客的风格保持一致,首先咱们先给出迪杰斯特拉算法的原理以及详细的示意图,而后根据这些示意图给出具体的实现方案。github
1、迪杰斯特拉算法原理解析算法
在博客的第一部分,咱们不谈任何与代码相关的内容,只谈迪杰斯特拉算法的原理以及生成最短路径的具体步骤。下方咱们会给出迪杰斯特拉算法的计算最短路径的每一步,并给出每一步具体的说明。废话少说,进入本部分的主题。数组
一、有向带权图数据结构
本篇博客依然采起咱们以前用的图的结构。不过咱们本篇博客使用的是有向图。下方这张图就是是咱们以前使用的无向图转换过来的,只是给以前的图的边添加的具体的方向,其余的并为改动。由无向图转换后的有向图以下所示,咱们将在下方的图的基础上计算出从A到D的最短路径。测试
2.迪杰斯特拉算法的具体步骤spa
下图就是求上图中A->D的最短路径时使用迪杰斯特拉算法的具体步骤。迪杰斯特拉算法主要核心思想是由起始结点开始,慢慢的由尾结点扩散。直到扩展到尾结点位置。下方咱们将根据每一个步骤给出具体的介绍:3d
(1)与起始结点A直接相连的点是B和F, 即 A->B的距离为10, A->F的距离为11, 因此咱们选择A->B这个路径。
(2)选择B结点后,咱们开始探测与B相连的结点,即 A->B->G路径长度为26, A->B->I路径长度为22, A->B->C路径长度为28,这三个与上一步留下的 A->F(11)相比,A->F(11)的距离最短,因此接下来要探测与F相连的结点。
(3)F可到达的点是G和E,因此 A->F->G的距离为27, A->F->E的距离为37。由于 A->F->G(27) 大于 A->B->G(26)这个路径,因此由A到G的路径咱们依然选择A->B->G(26)这个路径。从 A->B->G(26),A->B->I(22),A->B->C(28), A->F->E(37)。中选出最短那个距离就是 A->B->I(22),因此咱们将I做为咱们下次探测的结点。
(4)与I相连的就是D结点,因此咱们容易计算出 A->B->I->D这条路径的长度为 22+21=43。从 A->B->I->D(43), A->B->G(26), A->B->C(28), A->F->E(37)这些路径中咱们仍是选择最小的那一个,不难看出 A->B->G(26)这条路径在上述路径集合中最小,因此咱们将G结点做为下次路径的探测对象。
(5)G可到达的结点是H和D, 因此咱们能够获得两条新的路径 A->B->G->H(26+19=45)和 A->B->G->D(26+24=50),由于 A->B->G->D(50)这条路径的长度要大于 A->B->I->D(43)这条路径的长度,由于这两条路径都是从A到D,因此咱们选择较小的 A->B->I->D(43)路径。从 A->B->G->H(45),A->B->I->D(43), A->B->C(28), A->F->E(37)这几条路径中咱们依然选择最小的那条路径。从上面这几条数据咱们不难看出 A->B->C(28)这条路径最短,因此咱们下次要探测的点是C点。
(6)C点能够到达的点只有D点,因此咱们能够获得一条新的路径 A->B->C->D, 路径长度为50。由于从A到达D的路径还有 A->B->I->D(43),而 A->B->C->D(50)这条路径的长度要大一些,A到D点的路径依然选择 A->B->I->D(43)。C结点探测完毕,咱们从 A->B->G->H(45),A->B->I->D(43), A->F->E(37)几条候选路径中依然选择最小的那一个。不难看出 A->F->E(37)这条路径最小,因此下一步咱们要探测E结点。
(7)E结点能够到达的点为H和D,因此 A->F->E(37)这条路径能够延伸为 A->F->E->H(44)和A->F->E->G(57)两条边。由于A到H的路径还有一条为 A->B->G->H(45),而咱们刚生出的这一条要小于以前的那一条,因此A到H的路径更新为 A->F->E->H(44)。而A->F->E->G(57)这条A到D的路径要比 A->B->I->D(43)要大,因此不进行更新,A到D的路径依然采用A->B->I->D(43)。通过这一步后咱们将 A->F->E->H(44)和A->B->I->D(43)进行比较,较小的路径为 A->B->I->D(43),而D节点又是咱们的终点, 因此A到D的最短路径为A->B->I->D(43)。
三、迪杰斯特拉算法的数据表示对象
上面是咱们使用图形的方式给出了迪杰斯特拉算法的具体步骤,接下来咱们会把上面的步骤转换成数据的表示方式,以便于咱们使用程序进行计算。下方就是上面这些示意图的的完整的数据表示。下方矩阵中的数据标志着起始节点到该节点的距离。由上到下,以此增长距离的大小,而最上面一排的红色字母,标示着该结点的前驱。下方咱们在给出每个行数据的详细介绍。blog
第一行数据记录了A->B和A->F的信息,固然A->A的距离为0。其余尚不可达的点为-1。
第二行在第一行数据的基础上证据了 A->B->C, A->B->G, A->B->I的距离信息,由于从第一行数的比较咱们能够知道,A->B的距离要小于A->F的距离,因此咱们的最短路径要向着A->B->?上发展。
第三行的数据则是在第二行的数据上获得的,从第二行数据上咱们容易看出,A->F(11)要比A->B->(C, G, I)都要小,因此咱们的最短路径要向着A->F->?的放心发展。因此咱们找到了A->F->E(37)和A->F->G(28)这条路径。由于 A->B->G(26)小于A->F->G(28),因此A到G的路径不进行更新。
第四行的数据则是第三行数据的延伸,咱们能够知道A->B->I(2)在当前全部延伸的路径中最小,因此咱们要向着 A->B->I->? 方向发展。所以咱们找到了 A->B->I->D(43)这条路径。
以此类推……
其实上图就是咱们以前原理图的数据表示,接下来咱们就要根据这些原理图和数据图来给出咱们的代码实现,固然咱们仍是使用Swift语言来实现。
2、迪杰斯特拉算法的具体实现
1.上述原理总结
上面说这么多,简单的总结一下,上面整个过程无非就是下面这两步的循环,而循环结束的条件就是最短路径延伸到结束路径便可,也就是咱们本例中的D点。
第一步:比较已经发展的全部路径,找出目前最短的路径。
第二步:在上一步找到的最短路径的基础上发展新的路径,而后重复上一步,直到延伸到 end节点为止。
通过这么一分析,在给出具体的代码实现就显得简单多了,咱们的程序总体上来讲就是一个循环,里边包含着上面这两步。
2.具体代码实现
分析完原理后,接下来咱们就要开始实现咱们的代码了。下方就是咱们整个代码的具体实现。固然,咱们的图结构是以邻接链表的形式存储的有向联通图。具体代码实现以下所示:
(1)路径结构的存储
咱们先建立一个DistanceInfo类,该类中记录了一些距离信息。previousNoteIndex字段存储的是当前节点的前驱节点的索引,默认为-1。而distance存储的就是起始结点到该节点的距离,默认是最大距离。而isInPath则标记该结点是否位于已生成路径的上,若是是的话就是true, 若是不是就是false,默认是false。
(2)代码的总体结
下方代码块就是咱们迪杰斯特拉算法代码实现的总体结构。首先咱们会建立一个distanceInfos数组,数组中元素的个数就是图的结点的个数。其中存储的是DistanceInfo的对象,每一个数组索引对应的DistanceInfo对象中存储的信息就是起始结点到该结点的距离信息。首先咱们对起始结点的DistanceInfo对象进行初始化。
紧接着下方这个循环就是咱们上面所说的循环,循环结束的条件就是将最短路径延伸到end结点。循环中主要有两步,第一步是在当前循环的index对应的结点上发展新的路径,第二步就是在这些发展的路径中寻找最短路径,在这个新最短路径的基础上再次发展新的路径。
(3)发展新的路径
接下来咱们来看一下countCurrentNoteAllDistance()方法中的代码,也就是发展新的路径的代码。主要就是遍历邻接链表上当前索引所连的链表上的数据,将这些数据所对应的结点添加到咱们的路径上。往路径上添加新的结点是要注意一点,好比A->D是100, 以前B->D是80,若是如今的路径要比以前的路径要大就不更新,反之就更新咱们距离信息数组中的DistanceInfo对象。具体代码以下所示:
(4)、寻找最小路径
下方代码就是寻找当前已发展的全部路径中最短的那条路径,主要仍是对DistanceInfo数组的操做。下方代码,找到最短路径后,并把最短路径的索引进行返回。
上方就是咱们迪杰斯特拉算法代码实现的核心代码。
3、测试用例
实现完代码后,咱们就要对代码进行测试了。下方就是咱们的测试用例,该测试用例中建立的图是有向连通图,而且要求出节点A->D的最短路径。
上述测试用例的输入结果以下:
今天的博客就先到这儿,本篇博客所涉及的Demo也将会在github上进行分享,分享地址以下:
github分享地址:https://github.com/lizelu/DataStruct-Swift/tree/master/ShortestPathDijkstra