美文网首页程序员
Johnson 全源最短路径算法

Johnson 全源最短路径算法

作者: 某昆 | 来源:发表于2017-12-15 16:27 被阅读365次

    前言

    上一篇文章已经阐述了Floyd-Warshall算法,适用于存在负权重路径的稠密图。本文讲述的算法适用于稀疏图。

    全源最短路径求解其实是单源最短路径的推广,求解单源最短路径的两种算法时间复杂度分别为:

    • Dijkstra 单源最短路径算法:时间复杂度为 O(E + VlogV),要求权值非负;
    • Bellman-Ford 单源最短路径算法:时间复杂度为 O(VE),适用于带负权值情况;

    如果对全图顶点遍历,使用Dijkstra 算法,时间复杂度将变成O(VE + V2logV),看起来优于 Floyd-Warshall 算法的 O(V3)。不过,Dijkstra 算法要求权值重不能为负。

    Johnson 算法能调整权重为负的图,使之能够使用Dijkstra 算法。

    re-weight

    以下图为例,Johnson 算法对下图进行re-weight操作,使权重不为负,并且re-weight后,计算出来的最短路径仍然正确。

    首先,新增一个源顶点 ,并使其与所有顶点连通,新边赋权值为 0,如下图所示。

    接下来重新计算新增顶点到其它顶点的最短路径,利用单源最短路径算法,图中存在负权重节点,使用bellman ford算法,计算新增节点到其它节点的最短路径 h[],然后使用如下公式对所有边的权值进行 "re-weight":

    w(u, v) = w(u, v) + (h[u] - h[v]).

    对于此公式的证明请参考算法导论一书。

    现在除新增结点外,其它结点的相关边权重值都已经为正数了,可以将新增结点删除,对其它结点使用Dijkstra 算法了。

    实现

    Johnson 算法比Floyd-warshall算法更容易理解一些,实现较简单,代码如下。本博中所有代码均可见本人的github,欢迎访问。

    public void johnson(MatrixGraph graph){
        Vertex s = new Vertex("s");
        graph.addVertex(s);
        for (int i = 0; i < graph.mList.size(); i++) {
            graph.addEdge(s, graph.mList.get(i), 0);
        }
        //计算s点到其它点的最短距离
        ArrayList<Integer> h = bellman_ford(graph, s);
        //重新计算除s以外的其它点权重
        ArrayList<MatrixEdge> edges = new ArrayList<>();
        MatrixEdge temp = null;
        for (int i = 0; i < VERTEX_NUM; i++) {
            for (int j = 0; j < VERTEX_NUM; j++) {
                temp = graph.mEdges[i][j];
                if (temp != null && temp.v1 != s && temp.v2 != s) {
                    edges.add(temp);
                }
            }
        }
        
        System.out.println(" -------- ");
        
        for (int i = 0; i < edges.size(); i++) {
            temp = edges.get(i);
            temp.weight = temp.weight + h.get(graph.mList.indexOf(temp.v1)) - h.get(graph.mList.indexOf(temp.v2));
            System.out.print(temp + " | ");
        }
        System.out.println();
        System.out.println(" --------- ");
        
        graph.removeVertex(s);
        
        //根据重新计算的非负权重值,遍历调用dijkstra算法
        for (int i = 0; i < graph.mList.size(); i++) {
            dijkstra(graph, graph.mList.get(i));
        }
    }

    相关文章

      网友评论

        本文标题:Johnson 全源最短路径算法

        本文链接:https://www.haomeiwen.com/subject/hxjdwxtx.html