Dijkstra算法解析及Java代码实现(附图)

所谓单源,即求从一个点出发,到其他各点的最短路径,也就是说 :如果这个图有 n个点,我们要求 n-1个路径

 

算法概述

Dijkstra算法使用了广度优先搜索(BFS)解决赋权有向图或者无向图的单源最短路径问题,算法最终得到一个最短路径树。该算法常用于路由算法或者作为其他图算法的一个子模块。

该算法的时间复杂度是n的平方,可以使用堆优化。

但是,要注意一点,Dijkstra算法只能适用于权值为正的情况下;如果权值存在负数,则不能使用。
 

算法基本思想

     通过Dijkstra计算图G中的最短路径时,需要指定起点vs(即从顶点vs开始计算)。

     此外,引进两个集合SU

  • S的作用:记录已求出最短路径的顶点
  • U的作用:记录还未求出最短路径的顶点(以及该顶点到起点vs的距离)。

     初始时,S 中只有起点 vs;U 中是除 vs 之外的其他顶点,并且 U 中顶点的路径是"起点vs到该顶点的路径"。然后,从U中找出路径最短的顶点,并将其加入到 S 中;接着,更新 U 中的顶点和顶点对应的路径。 然后,再从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 ... 重复该操作,直到遍历完所有顶点。


算法具体操作步骤

(1) 初始时,S只包含起点 vs;U 包含除 vs 外的其他顶点,且U中顶点的距离为"起点vs到该顶点的距离"。

     例如,U中顶点v的距离为 (vs,v) 的长度,然后 vs 和 v 不相邻,则 v 的距离为∞。

(2) 从 U 中选出"距离最短的顶点 k",并将顶点 k 加入到 S 中;同时,从 U 中移除顶点 k。

(3) 更新 U 中各个顶点到起点 vs 的距离。之所以更新 U 中顶点的距离,是由于上一步中确定了 k 是求出最短路径的顶点,从而可以利用 k 来更新其它顶点的距离;例如,(vs,v) 的距离可能大于 (vs,k)+(k,v) 的距离。

(4) 重复步骤(2)和(3),直到遍历完所有顶点。

图示:这里的起点 vs 是 顶点D

伪代码:

代码:

import java.util.List;
import java.util.ArrayList;

public class ShortestPathDijkstra {
    /** 邻接矩阵 */
    private int[][] matrix;
    /** 表示正无穷 */
    private int MAX_WEIGHT = Integer.MAX_VALUE;
    /** 顶点集合 */
    private String[] vertexes;
 
    /**
     * 创建图2
     */
    private void createGraph2(int index) {
        matrix = new int[index][index];
        vertexes = new String[index];
        
        int[] v0 = { 0, 1, 5, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };
        int[] v1 = { 1, 0, 3, 7, 5, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };
        int[] v2 = { 5, 3, 0, MAX_WEIGHT, 1, 7, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };
        int[] v3 = { MAX_WEIGHT, 7, MAX_WEIGHT, 0, 2, MAX_WEIGHT, 3, MAX_WEIGHT, MAX_WEIGHT };
        int[] v4 = { MAX_WEIGHT, 5, 1, 2, 0, 3, 6, 9, MAX_WEIGHT };
        int[] v5 = { MAX_WEIGHT, MAX_WEIGHT, 7, MAX_WEIGHT, 3, 0, MAX_WEIGHT, 5, MAX_WEIGHT };
        int[] v6 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 3, 6, MAX_WEIGHT, 0, 2, 7 };
        int[] v7 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 9, 5, 2, 0, 4 };
        int[] v8 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 7, 4, 0 };
        matrix[0] = v0;
        matrix[1] = v1;
        matrix[2] = v2;
        matrix[3] = v3;
        matrix[4] = v4;
        matrix[5] = v5;
        matrix[6] = v6;
        matrix[7] = v7;
        matrix[8] = v8;
        
        vertexes[0] = "v0";
        vertexes[1] = "v1";
        vertexes[2] = "v2";
        vertexes[3] = "v3";
        vertexes[4] = "v4";
        vertexes[5] = "v5";
        vertexes[6] = "v6";
        vertexes[7] = "v7";
        vertexes[8] = "v8";
    }
    
    /**
     * 创建图1
     */
    private void createGraph1(int index) {
        matrix = new int[index][index];
        vertexes = new String[index];
 
        int[] v0 = { 0, 1, MAX_WEIGHT, MAX_WEIGHT, 2, MAX_WEIGHT };
        int[] v1 = { 1, 0, 1, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };
        int[] v2 = { MAX_WEIGHT, 1, 0, 1, MAX_WEIGHT, MAX_WEIGHT };
        int[] v3 = { MAX_WEIGHT, MAX_WEIGHT, 1, 0, 1, MAX_WEIGHT };
        int[] v4 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 1, 0, 1 };
        int[] v5 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 1, 1, 0 };
 
        matrix[0] = v0;
        matrix[1] = v1;
        matrix[2] = v2;
        matrix[3] = v3;
        matrix[4] = v4;
        matrix[5] = v5;
 
        vertexes[0] = "A";
        vertexes[1] = "B";
        vertexes[2] = "C";
        vertexes[3] = "D";
        vertexes[4] = "E";
        vertexes[5] = "F";
    }
 
    /**
     * Dijkstra最短路径。
     * 
     * vs -- 起始顶点(start vertex) 即,统计图中"顶点vs"到其它各个顶点的最短路径。
     */
    public void dijkstra(int vs) {
        // 建立flag数组, 长度为顶点数目, flag[i]=true表示"顶点vs"到"顶点i"的最短路径已成功获取
        boolean[] flag = new boolean[vertexes.length];
        // S集合:记录已求出最短路径的顶点
        String[] S = new String[vertexes.length];
        // U集合:记录还未求出最短路径的顶点(以及该顶点到起点s的距离),与 flag配合使用,flag[i] == true 表示U中i顶点已被移除
        int[] U = new int[vertexes.length];
        // 前驱顶点数组,即:prev[i]的值是"顶点vs"到"顶点i"的最短路径所经历的全部顶点中,位于"顶点i"之前的那些顶点。
        int[] prev = new int[vertexes.length];
        
 
        // 步骤一:初始时,S中只有起点vs;U中是除vs之外的顶点,并且U中顶点的路径是"起点vs到该顶点的路径"。
        for (int i = 0; i < vertexes.length; i++) {
            flag[i] = false; // 顶点i的最短路径还没获取到(初始化为false)。
            U[i] = matrix[vs][i]; // 顶点i与顶点vs的初始距离为"顶点vs"到"顶点i"的权。也就是邻接矩阵vs行的数据。
            
            prev[i] = 0; //顶点i的前驱顶点为0
        }
 
        // 将vs从U中“移除”(U与flag配合使用)
        flag[vs] = true;  //flag[i] == true 表示U中i顶点已被移除
        U[vs] = 0;  //起点和自己本身的距离是0
        // 将vs顶点加入S
        S[0] = vertexes[vs];
        // 步骤一结束
        
        //步骤四:重复步骤二三,直到遍历完所有顶点。
        // 遍历vertexes.length-1次;每次找出一个顶点的最短路径。
        int k = 0;
        for (int i = 1; i < vertexes.length; i++) {
            // 步骤二:从U中找出路径最短的顶点,并将其加入到S中(如果vs顶点到x顶点还有更短的路径的话,
            // 那么必然会有一个y顶点到vs顶点的路径比前者更短且没有加入S中
            // 所以,U中路径最短顶点的路径就是该顶点的最短路径)
            // 即,在未获取最短路径的顶点中,找到离vs最近的顶点(k)。
            int min = MAX_WEIGHT;
            for (int j = 0; j < vertexes.length; j++) {
                if (flag[j] == false && U[j] < min) {
                    min = U[j];
                    k = j;
                }
            }
            
            //将k放入S中
            S[i] = vertexes[k];
            
            //步骤二结束
            
            
            //步骤三:更新U中的顶点和顶点对应的路径
            //标记"顶点k"为已经获取到最短路径(更新U中的顶点,即将k顶点对应的flag标记为true)
            flag[k] = true;
            
            //修正当前最短路径和前驱顶点(更新U中剩余顶点对应的路径)
            //即,当已经"顶点k的最短路径"之后,更新"未获取最短路径的顶点的最短路径和前驱顶点"。
            for (int j = 0; j < vertexes.length; j++) {
                //以k顶点所在位置连线其他顶点,判断其他顶点经过最短路径顶点k到达vs顶点是否小于目前的最短路径,是,更新入U,不是,不做处理
                int tmp = (matrix[k][j] == MAX_WEIGHT ? MAX_WEIGHT : (min + matrix[k][j]));
                if (flag[j] == false && (tmp < U[j])) {
                    U[j] = tmp;
                    //更新 j顶点的最短路径前驱顶点为k
                    prev[j] = k;
                }
            }
            //步骤三结束
        }
        //步骤四结束
 
        // 打印dijkstra最短路径的结果
        System.out.println("起始顶点:" + vertexes[vs]);
        for (int i = 0; i < vertexes.length; i++) {
            System.out.print("最短路径(" + vertexes[vs] + "," + vertexes[i] + "):" + U[i] + "  ");
            
            List<String> path = new ArrayList<>();
            int j = i;
            while (true) {
                path.add(vertexes[j]);
                
                if (j == 0)
                    break;
                
                j = prev[j];
            }
            
            for (int x = path.size()-1; x >= 0; x--) {
                if (x == 0) {
                    System.out.println(path.get(x));
                } else {
                    System.out.print(path.get(x) + "->");
                }
            }
            
        }
        
        System.out.println("顶点放入S中的顺序:");
        for (int i = 0; i< vertexes.length; i++) {
            
            System.out.print(S[i]);
            
            if (i != vertexes.length-1) 
                System.out.print("-->");
        }
            
    }
 
    public static void main(String[] args) {
        ShortestPathDijkstra dij = new ShortestPathDijkstra();
//        dij.createGraph1(6);
        dij.createGraph2(9);
        dij.dijkstra(0);
    }
 
}

其中代码中所调用的两张图片:

图一:

图二:

结果:

图一:

图二:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值