图的最短路径 -- 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



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值