[ 数据结构 ] 弗洛伊德算法(Floyd)--------最短路径问题

0 Floyd算法介绍

  1. 和 Dijkstra 算法一样,弗洛伊德(Floyd)算法也是一种用于寻找给定的加权图中顶点间最短路径的算法。该算法名称以创始人之一、1978 年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名
  2. 弗洛伊德算法(Floyd)计算图中各个顶点之间的最短路径
  3. 迪杰斯特拉算法用于计算图中某一个顶点到其他顶点的最短路径。
  4. 弗洛伊德算法 VS 迪杰斯特拉算法:迪杰斯特拉算法通过选定的被访问顶点,求出从出发访问顶点到其他顶点的最短路径;弗洛伊德算法中每一个顶点都是出发访问点,所以需要将每一个顶点看做被访问顶点,求出从每一个顶点到其他顶点的最短路径。
  5. 回顾:迪杰斯特拉算法

1 Floyd算法应用

[ 数据结构 ] 弗洛伊德算法(Floyd)--------最短路径问题_第1张图片

最短路径问题:

  1. 胜利乡有 7 个村庄(A, B, C, D, E, F, G)
  2. 各个村庄的距离用边线表示(权) ,比如 A – B 距离 5 公里
  3. 问:如何计算出各村庄到 其它各村庄的最短距离?

思路分析:

  1. 核心思想:穷举
  2. 三层for循环分别使3个指针指向三个顶点,最外层表示中间顶点k
  3. k=0则表示A顶点作为中间顶点,那么就可以更新BC和CG间的最短路径
  4. k从0->6,则可以得到所有顶点间的最短路径
  5. 当然想法简单,但是实现起来需要借助辅助变量,这里用到初始值为邻接矩阵的距离表dis和初始值为出发点的前驱关系表pre

[ 数据结构 ] 弗洛伊德算法(Floyd)--------最短路径问题_第2张图片

[ 数据结构 ] 弗洛伊德算法(Floyd)--------最短路径问题_第3张图片

个人理解:

  1. 为什么如此初始化前驱关系表?

    因为默认开始只有两种路线:直连/不直连,两种情况下默认前驱节点都是出发点,即出发点到任何顶点的前驱节点都是自己,至于后续的改动,不管最短路线改为非直连的两段还是三段,也只是针对不直连的路(即65535),而直连的两个顶点相关数据无需改动,前驱自然就是终点的前驱节点,即出发点

  2. 两顶点之间非直连,最短路径为3段的,最短路径是怎么更新的,如CD的最短路径为CEFD?

    观察:虽然F作为中间点,但是测试时中间点第一个就用F,CD的距离和前驱都未能更新,仅更新了DE之间和DG之间数据,而当F之后的E作为中间点,CD之间数据才能更新,即CD的前驱为F,为什么会如此?

    注意:这是因为F作为中间点时,CEFD中存在65535,即CF间距此时还是65535所以CD无法更新,而E作为中间点时之前更新了ED=EF+FD,所以CD=CE+ED,CD相关数据才得以更新

    结论:当MN两点之间最短路径需要经过n个顶点,那么只有当这n个中间点的相关数据全部更完,MN间的数据才能更新,也可以说,想更新MN的数据,必须保证路径上没有65535

//弗洛伊德算法:最短路径问题
public class FloydAlgorithm {
    public static void main(String[] args) {
        //测试看看图是否创建成功
        char[] vertex = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
        //创建邻接矩阵
        int[][] matrix = new int[vertex.length][vertex.length];
        final int N = 65535;
        matrix[0] = new int[] { 0, 5, 7, N, N, N, 2 };
        matrix[1] = new int[] { 5, 0, N, 9, N, N, 3 };
        matrix[2] = new int[] { 7, N, 0, N, 8, N, N };
        matrix[3] = new int[] { N, 9, N, 0, N, 4, N };
        matrix[4] = new int[] { N, N, 8, N, 0, 5, 4 };
        matrix[5] = new int[] { N, N, N, 4, 5, 0, 6 };
        matrix[6] = new int[] { 2, 3, N, N, 4, 6, 0 };
        Graph graph = new Graph(vertex, matrix);
        graph.floyd();
        graph.show();
    }
}

class Graph {
    private char[] vertex;
    private int[][] dis;
    private int[][] pre;

    public Graph(char[] vertex, int[][] matrix) {
        this.vertex = vertex;
        //初始化最短路径数组为邻接矩阵,直连就是最短路径,非直连则需要找
        this.dis = matrix;
        pre = new int[vertex.length][vertex.length];
        for (int i = 0; i < pre.length; i++) {
            //这里为什么如此初始化前驱节点?
            //答:因为默认开始只有两种路线:直连/不直连,两种情况下默认前驱节点都是出发点,即出发点到任何顶点的前驱节点都是自己
            //  至于后续的改动,不管最短路线改为非直连的两段还是三段,也只是针对不直连的路(即65535),
            //  而直连的两个顶点相关数据无需改动,前驱自然就是终点的前驱节点,即出发点
            Arrays.fill(pre[i], i);
        }
    }

    public void show() {
        for (int i = 0; i < vertex.length; i++) {
            for (int p : pre[i]) {
                System.out.print(vertex[p]+" ");
            }
            System.out.println();
            for (int j = 0; j < vertex.length; j++) {
                System.out.print("["+vertex[i]+"->"+vertex[j]+"="+dis[i][j]+"] ");
            }
            System.out.println("\n");
        }
    }

    public void floyd() {
        int len = 0;
        //问题:两顶点之间非直连,最短路径为3段的,最短路径是怎么更新的,如CD的最短路径为CEFD?
        //答:
        //  观察:虽然F作为中间点,但是测试时中间点第一个就用F,CD的距离和前驱都未能更新,仅更新了DE之间和DG之间数据,
        //      而当F之后的E作为中间点,CD之间数据才能更新,即CD的前驱为F,为什么会如此?
        //  注意:这是因为F作为中间点时,CEFD中存在65535,即CF间距此时还是65535所以CD无法更新,而E作为中间点时之前更新了ED=EF+FD
        //      所以CD=CE+ED,CD相关数据才得以更新
        //  结论:当MN两点之间最短路径需要经过n个顶点,那么只有当这n个中间点的相关数据全部更完,MN间的数据才能更新
        //      也可以说,想更新MN的数据,必须保证路径上没有65535
        int[] range = {5,4,3,2,1,0,6};
        for (int k : range) {
            //BC CG
            System.out.println(vertex[k] + "作为中间节点");
            for (int i = 0; i < vertex.length; i++) {
                for (int j = 0; j < vertex.length; j++) {
                    len = dis[i][k] + dis[k][j];
                    if (len < dis[i][j]) {
                        System.out.println("修改了:[" + vertex[i] + "-" + vertex[j] + "]");
                        dis[i][j] = len;
                        //直连的前驱(出发点)改为非直连后半段(最后一段)的前驱(出发点)
                        //举例:AE>AG+GE,所以A(pre[AE])=G(pre[GE])
                        pre[i][j] = pre[k][j];
                    }
                }
            }
        }
    }
}

你可能感兴趣的:(数据结构与算法,算法,数据结构,图论)