数据结构-最短路径

175 阅读3分钟

前言

在图的实际应用中,很多情况下边是存在权值的。尽管我们之前通过学习已经知道,DFS是可以实现求一个顶点到另一个顶点的最短路径的,但是那是在各条边权值一致且为正数时才能求得,而大多数情况下各条边的权值是不一样的。

要想求得顶点到顶点的最短路径,我们一般有两种算法:一种叫Dijkstra算法,它适用于求单一顶点到全部顶点的最短路径问题;另一种叫Floyd算法,它适用于求图中每对顶点的最短路径问题。

Dijkstra(迪杰斯特拉)算法

原理

Dijkstra算法概括来说就是从一个顶点a开始,将顶点放到顶点集V中。当V中并入一个新顶点时,重新更新顶点a和顶点集之间的最短路径。比如说,此时顶点集{a,b}并入了一个新顶点c,此时a到b的路径并不如a到c再到b更短,所以要更新最短路径值。

在Dijkstra算法中,还需要两个数组用来帮助算法的实现,分别定义为min_len[]track[]

min_len[]用于记录图中某一顶点a到各个顶点的实时最短路径。当顶点集中只有a时(也就是该数组一开始初始化时),其中的值分别是顶点a到其他顶点的边的权值,如果到该顶点没有边,就将其置为无限(9999)。

track[]用于记录源顶点a到其他顶点时最短路径的遍历顶点,以方便回溯顶点信息求得最短路径。

Dijkstra算法的实现步骤如下:

  1. 将算法当中的顶点集和各个辅助数组初始化:将最初的顶点a加入到顶点集V当中,并初始化min_len[]数组;
  2. 找到距离顶点a最近的顶点。将其加入顶点集V中,并更新所有与它相邻的节点的距离(如果新的距离比之前的距离更短);
  3. 重复第2步,直到所有顶点都被加入到顶点集中,或者是图中没有与顶点a相连的顶点;
  4. min_len[]中到所有顶点的距离,即为最短路径。

性能与特点

时间复杂度:Dijkstra的时间复杂度为O(V2)O(V^2)

特点:

  • Dijkstra算法基于贪心算法实现;
  • 如果想求某顶点到指定顶点的最短路径,仍然需要遍历所有顶点的最短路径,时间复杂度不变;
  • 该方法仅适用于权值为正的图,如果图中有边权值为负,则无法使用该算法。

Floyd(弗洛伊德)算法

原理

Floyd算法的实现相对于Dijkstra算法要简单一些,而且相较于Dijkstra算法,它可以应用于有权值为负的边的图上。以下是Floyd算法的实现步骤:

  1. 将所有顶点之间的距离设为无限(9999),并将对角线上的顶点之间的距离设为0;
  2. 设置一个三重循环,外层循环遍历所有顶点,中间循环遍历所有起点,内层循环遍历所有终点。在每次遍历中,如果从起点经过中间顶点到达终点的路径比直接从起点到达终点的路径更短,则更新两者之间的距离;
  3. 扫描所有顶点的距离,找到任意两个顶点之间的最短路径,即为所求。

性能与特点

时间复杂度:Floyd算法的时间复杂度为O(V3)O(V^3),属于时间复杂度很高的算法。

特点:

  • Floyd算法基于动态规划思想实现;
  • 相较于Dijkstra算法,它可以求得两个指定顶点之间的最短距离,并且可以对带有负权值的边进行求最短路径的操作;
  • 缺点非常明显,时间复杂度太高,只适用于顶点较少的图。