单源最路径算法总结

1.相关概念

         带权有向图上顶点u→v间的最短路的权为:

                  

         负权值边、负权回路及正权回路

         最短路径的表示:最短路径树

         类似于广度优先搜索得出的广度优先树,增加一个前驱数组π[V],可以依次求出源点到各点的最短路径;在算法结束时,如下的Gπ就是最短路径树。

         由π值可以推导出前驱子图Gπ=(Vπ,Eπ),其顶点集Vπ为G中所有具有非空前驱的顶点集合,再添加上源点s:Vπ={v∈V:π[v]≠NIL}∪{s},有向边集Eπ={(π[v],v):v∈Vπ-{s}}.

         松弛技术

         增加一个数据结构d[V]:描述s→v的最短路径上权值的上界。

         最短路径估计和前驱初始化函数(复杂度为Θ(V))

INITIALIZE-SINGLE-SOURCE(G,s)
    for each vertex v∈G.V
        d[v] = ∞
        π[v] = NIL
    d[s] = 0

         对一条权值为w的边(u,v)的松弛过程如下:

RELAX(u,v,w)
    if d[v]>d[u]+w(u,v)
        d[v] = d[u]+w(u,v)
        π[v] = u

2.求单源最短路径的Bellman-Ford算法

  原理:

  运用松弛技术,对每个顶点v,逐步减少从源s→v的最短路权的估计值d[v]直到其达到实际最短路径的权δ(s,v)为止。 

ELLMAN-FORD(G,w,s)
    INITIALIZE-SINGLE-SOURCE(G,s)
    
    for i= 1 to |G.V|-1
        for each edge (u,v)∈G.E
            RELAX(u,v,w)
    
    //判断是否包含源点可达的负权回路        
    for each edge (u,v)∈G.E
        if d[v]>d[u]+u(v,w)
            return false;
    return true

  说明:

   1)实现的关键,每一趟RELAX都必须按照任一固定的顺序进行(实际并不需要,这样只是利于显示),每一次循环都松弛了所有的|G.E|条边,这相当于s=v0→v=vk(v为任一点)的最短路径p=<v0,v1,…,vk>可以依次被松弛,即第i次循环包含对边(vi-1,vi)的松弛,这样可确定d[v]=d[vk]=δ[s, vk]=δ[s, v];

    2)本算法可以处理有负边、负回路的情况;

    3)算法的时间复杂对为O(VE).

3.有向无回路(dag)图中的单源最短路径

         原理:BELLMAN-FORD算法因为没有按照路径顺序所以每次循环都是对松弛所有的|E|条边,如果对顶点进行拓扑排序,则对顶点可按照拓扑顺序进行松弛,如果u到v存在一条路径,则在拓扑序列中u先于v。在拓扑排序的过程中我们仅对顶点执行了一趟操作。当对每个顶点处理时,松弛从该顶点出发的所有边。这相当于减少了松弛的边数,每条边总共被松弛一次;

         算法:

DAG-SHORTEST-PATHS(G,w,s)
    topologically sort the vertices of G
    INITIALIZE-SINGLE-SOURCE(G,s)
    for each vertex u, taken in topologically sorted order
        for each vertex v∈G.Adj[u]
            RELAX(u,v,w)

      说明:

  1)算法的时间复杂度为Θ(V+E),此时图是用邻接表表示的;

  2)算法可以处理存在负权边的情况;

     3)算法有一个重要应用时确定dag图的关键路径。关键路径是通过dag的一条最长路径,它对应于一个有序的工作序列的最长时间;有两种方法:对边的权去负值,运行一遍本过程;初始化过程∞换为-∞,RELAX中>换成<号;

4.Dijkstra算法

  原理:

  贪心法,类似于最小生成树算法。设置了一顶点集合S,从源点s到集合S中的顶点的最终最短路径的权值均已确定。算法反复选择具有最短路径估计d[u]的顶点u∈V-S,并将u加入到S中。

  算法:

  用到的数据结构:按关键之d排序的最小优先队列Q

IJKSTRA(G,w,s)

//初始化
    INITIALIZE-SINGLE-SOURCE(G,s)
    S = ∅
    Q = G.V
    
    while Q≠∅
        u = EXTRACT-MIN(Q)
        S = S∪{u}
        for each vertex v∈G.Adj[u]
            RELAX(u,v,w)

  说明:

  1)本算法只能用于无负权边、无负回路的情况;无负回路的要求是显然的,对于无负权边,这是因为本算法是以最短路径估计的贪心法,所以当前选择加入S的顶点u只能确定是此时u的最短路径估计,如果没有负权边的话,则可以保证该值就是此时的最短路径权值,否则的话,则之后如果有一复权值边则可能使的该点的最短路径估计值变小;

  2)算法终止时,前驱子图Gπ是以s为根的最短路径树;

  3)类似于Prim算法,算法的时间复杂度依赖于图的存储结构和优先队列的具体实现:

Minimum edge weight data structure

Time complexity (total)

Searching, adjacency matrix

O(V2+V)= O(V2)

binary heap, adjacency list

O((V+E)logV) = O(ElogV)

Fibonacci heap, adjacency list

O(E+VlogV)

  4)同广度优先搜索算法、prim算法有异同

     与广度优先算法的相似之处在于:前者的集合S相当于后者的褐色顶点集合,正如集合S中的顶点有着最终的最短路径权值,后者的黑色顶点集合也有着正确的广度优先距离;

     与Prime算法的相似之处在于:两种算法均采用最小优先队列来找出给定集合以外“最轻”的顶点,然后把该顶点加入到集合中,并相应地调整该集合以外剩余顶点的权;

     与Prim算法的不同之处在于:Dijkstra算法每次加入S的为具有最短路径估计的顶点,而Prim算法加入集合A的为具有与A相连最小权值边的顶点;另外,前者在添加顶点u之后,进行的是对u邻接点的RELAX操作,而后者进行的是确定最小权值边的操作。

参考文献:《算法导论》

原文地址:https://www.cnblogs.com/lyfruit/p/3078065.html