图的最短路径 -- Dijkstra 算法详解

经典的算法,求两个城市间的最短距离。


治学之道,最忌知其然而不知其所以然。所以今天就让我们来一步一步了解图,最短路径算法Dijkstra

以下是来自经典算法书籍 § Algorithms 的图的Java表示:

Princeton Graph.java

Bag.java

以下是Dijkstra 算法的证明:

Dijkstra's Algorithm的证明

麻省理工大学算法公开课:Dijkstra 算法-麻省理工大学

其实看完最短路径算法第一节课就大概明白了Dijkstra算法的原理。


这里存在一个假设:我们不存在任何的负权边。如果有负权边,我们是不能使用Dijkstra算法的。因为它使用的是贪心算法。


证明: 维护一个已经是最短距离顶点的set1, 以及其它顶点的set.

每次搜索余下的所有的点,找到一个最短距离的顶点,将它加到set1。为什么它就是最短距离呢?

Proof:

    假设找到的这个点是A点,假如如果我们可以从set1中到B点再到A点,因为dist[src][B] > dist[src][A],

所以dist[src][B] + dist[B][A] >= dist[src][A],所以经过B的路径还是比从set直接到A要远。因此我们找到的从原点到A的路径就是最短路径。

这样就可以把A点加入到set1中。


set1中加入A点后,我们要把与A相接的所有的点的距离值全部更新一次(即所有的点都算一次经过A点的距离。)这样我们可以更新从set1出发到其它点的距离。


以下是代码。在此代码中,我们使用邻接矩阵来表示图,如果是稀疏图,其实应该用邻接链表来表示,也就是前面所述的Bag,这里就不详述啦:

package summarize;

import java.util.ArrayList;

public class GraphDemo {
    private static int M = Integer.MAX_VALUE;  // 表示此路不可通
    
    // http://www.geeksforgeeks.org/greedy-algorithms-set-6-dijkstras-shortest-path-algorithm/
    // 以上为不能再优美的C语言实现。
    public static void main(String[] args) {
        int[][] w = {  // 用邻接表矩阵表示的无向图
                {0, 3, 2000, 7, M},
                {3, 0, 4, 2, M},
                {M, 4, 0, 5, 4},
                {7, 2, 5, 0, 6},
                {M, M , 4, 6, 0}
        };
        
        int[][] w2 = {  // 用邻接表矩阵表示的无向图
                {0, 10, M, 30, 100},
                {M, 0, 50, M, M},    
                {M, M, 0, M, 10},    
                {M, M, 20, 0, 60},    
                {M, M, M, M, 0}
        };
        
        int start = 0;
        
        int[] shortPath = dijkstra(w2, start);
        
        for (int i = 0; i < shortPath.length; i++) {
            System.out.println("The shortest path length from start to " + i + " is:" + shortPath[i]);
        }
    }
    
    public static int[] dijkstra(int[][] graph, int src) {
        if (graph == null || graph.length == 0 || graph[0].length == 0) {
            return null;
        }
        
        // get to know the number of the vertexs.
        int v = graph.length;
        
        // We need a indicator to know if we have visited the vertex.
        boolean visit[] = new boolean[v];
        
        // record the length result.
        int[] pathLen = new int[v];
        
        // record the path.
        ArrayList> path = new ArrayList>();
        for (int i = 0; i < v; i++) {
            path.add(new ArrayList());
            path.get(i).add(0);
            path.get(i).add(i);
        }
        
        // setup the source vertex;
        visit[0] = true;
        pathLen[0] = 0;
        
        // stop when all the vertices has been added into the result set.
        for (int i = 0; i < v - 1; i++) {
            
            int minLen = M;
            int minIndex = -1;
            for (int j = 0; j < v; j++) {
                // sometimes there is no route, so just let equal also return.
                // so we use graph[src][j] <= minLen
                if (!visit[j] && graph[src][j] <= minLen) {
                    minLen = graph[src][j];
                    minIndex = j;
                }
            }
            
            // get the new shortest path, add it into the solution set.
            visit[minIndex] = true;
            pathLen[minIndex] = graph[src][minIndex];
            
            // update all the neighbors of the new vertex.
            for (int k = 0; k < v; k++) {
                // if the path which pass the minIndex is shorter than the former path, 
                // just update it.
                if (!visit[k]  
                        && graph[src][minIndex] != M
                        && graph[minIndex][k] != M
                        && graph[src][minIndex] + graph[minIndex][k] < graph[src][k]) {
                    graph[src][k] = graph[src][minIndex] + graph[minIndex][k];
                    
                    path.set(k, new ArrayList(path.get(minIndex)));
                    path.get(k).add(k);
                }
            }
        }
        
        for (ArrayList array: path) {
            System.out.println(array.toString());
        }
        
        return pathLen;
    }

}

测试结果:

[0, 0]

[0, 1]

[0, 3, 2]

[0, 3]

[0, 3, 2, 4]

The shortest path length from start to 0 is:0

The shortest path length from start to 1 is:10

The shortest path length from start to 2 is:50

The shortest path length from start to 3 is:30

The shortest path length from start to 4 is:60



你可能感兴趣的:(公司面经)