• 最短路径Floyd算法剖析


    微博:http://weibo.com/375061590

    QQ :375061590

    到两个重要矩阵:

            1.d[numVex][numVex]  (numVex图的顶点数):最开始该矩阵就是图的邻接矩阵,经过Floyd算法处理开后,d[numVex][numVex]中的d[i][j],表示着从顶点i到j的最短路径的权重。

            2.p[numVex][numVex]:p[i][j]表示从i到j的最短路径上 i的后继,例如1到5最短路劲为1-2-4-5  那么p[1][5]==2 ,最开始构建的p矩阵中p[i][j]= j

    算法核心思想:  三圈for循环 

     1 for (int k = 0; k < graph.getNumVex(); k++) {
     2 
     3                      for (int v = 0; v < graph.getNumVex(); v++) {
     4 
     5                             for (int w = 0; w < graph.getNumVex(); w++) {
     6 
     7                                    if (d[v][w] > d[v][k] + d[k][w]) {
     8 
     9                                           d[v][w] = d[v][k] + d[k][w];
    10 
    11                                           p[v][w] = p[v][k];// p[v][w]是v--w最短路径上 v的下一顶点
    12 
    13                                    }
    14 
    15                             }
    16 
    17                      }
    18 
    19               }
    20 
    21  

    第一层 k是作为中间顶点

    第二层 v是作为起始顶点

    第三层 w是作为终点顶点

    内层核心代码

    以v为起点,w为终点,再以k作为v和w之间的中间点,去判断d[v][ w]和d[v][k] + d[k][w]的大小关系,如果d[v][w] > d[v][k] + d[k][w],说明找到从v→w的更短路径了,此时更改d[v][w]的值为d[v][k] + d[k][w]。

    p[v][w]的值也要相应改成p[v][k]的值,因为 p[v][k]的值是v→k最短路径上v的后继顶点,而v→w这段最短路径是连接在v→k这段路径后面的,所以令所当然p[v][w]也要指向p[v][k]。

    注意:最外层的k循环,前面的n此循环的结果跟后面n+1次循环的错做过程是息息相关,

           三次循环完成后,各个顶点之间的最短路径权重会存储在d矩阵中:d[i][j]表示i→j的最短路径权重。

    p中则存储着路径上的顶点,如果把i→j最短路径上的顶点列出来呢?

    代码:

     1  
     2 
     3 //求start→end 最短路径上的顶点
     4 
     5 StringBuilder path = new StringBuilder();
     6 
     7 int index = start;//起始点
     8 
     9 path.append(start + "");
    10 
    11               while (index != end) {
    12 
    13               //循环取出路径上的各个顶点
    14 
    15                      index = p[index][end];
    16 
    17                      if(index != end){
    18 
    19 path.append(index + "");
    20 
    21 }

    用一个while循环循环 index = p[index][end];直到达到终点

    假设该最短路径为 start→A→B→C→end

    则执行过程是:

     
    
    index = p[start][end]; →A
    
    path== start→A →
    
     
    
    index = p[A][end]; →B
    
    path== start→A →B →
    
    
    
    index = p[B][end]; →C
    
    path== start→A →B →C→
    
    
    
    index = p[C][end]; →end
    
    path== start→A →B →C→end

    测试:

      

    请输入定点的数目:5
    顶点数为:5
    请输入边数:7
    边数为:7
    请输入(Vi,Vj)上下标i 和  j,以及权重,用逗号隔开
    0,1,5
    0,4,7
    1,2,4
    4,2,8
    1,3,2
    2,3,6
    4,3,1
    初始的d矩阵
     
    0 5 9999 9999 7
     
    5 0 4 2 9999
     
    9999 4 0 6 8
     
    9999 2 6 0 1
     
    7 9999 8 1 0
     
    初始的p矩阵
     
    0 1 2 3 4
     
    0 1 2 3 4
     
    0 1 2 3 4
     
    0 1 2 3 4
     
    0 1 2 3 4
     
    处理后的d矩阵
     
    0 5 9 7 7
     
    5 0 4 2 3
     
    9 4 0 6 7
     
    7 2 6 0 1
     
    7 3 7 1 0
     
    处理后的p矩阵
     
    0 1 1 1 4
     
    0 1 2 3 3
     
    1 1 2 3 3
     
    1 1 2 3 4
     
    0 3 3 3 4
     
    求最短路径
    请输入起点:
    0
    请输入终点:
    2
    从0到2的最短路径为9
    该路劲为:0 → 1 →2
    是否继续计算其他最短路径 Y/N?
    y
    求最短路径
    请输入起点:
    0
    请输入终点:
    3
    从0到3的最短路径为7
    该路劲为:0 → 1 →3
    是否继续计算其他最短路径 Y/N?
    y
    求最短路径
    请输入起点:
    4
    请输入终点:
    1
    从4到1的最短路径为3
    该路劲为:4 → 3 →1
    是否继续计算其他最短路径 Y/N?
    y
    求最短路径
    请输入起点:
    2
     
    请输入终点:
    4
    从2到4的最短路径为7
    该路劲为:2 → 3 →4
    是否继续计算其他最短路径 Y/N?
    package DataStructure;
    
    import java.util.Scanner;
    
    public class Floyd {
        private Graph graph;
        private int[][] d;// 用来存储顶点到顶点之间最短路径的权重
        private int[][] p;// p[1][5]表示1到5的最短路径上 1的后继,例如1到5最短路劲为1-2-4-5 那么p[1][5]==2
    
        public Floyd() {
            this.graph = new Graph();
            d = graph.getArc();
            p = new int[graph.getNumVex()][graph.getNumVex()];
            initP();// 初始化矩阵p
            System.out.println("初始的d矩阵\n");
            for (int i = 0; i < graph.getNumVex(); i++) {
                for (int j = 0; j < graph.getNumVex(); j++) {
                    System.out.print(d[i][j] + " ");
                }
                System.out.println("\n");
            }
            System.out.println("初始的p矩阵\n");
            for (int i = 0; i < graph.getNumVex(); i++) {
                for (int j = 0; j < graph.getNumVex(); j++) {
                    System.out.print(p[i][j] + " ");
                }
                System.out.println("\n");
    
            }
            work();
            
            System.out.println("处理后的d矩阵\n");
            for (int i = 0; i < graph.getNumVex(); i++) {
                for (int j = 0; j < graph.getNumVex(); j++) {
                    System.out.print(d[i][j] + " ");
                }
                System.out.println("\n");
            }
            
            System.out.println("处理后的p矩阵\n");
            for (int i = 0; i < graph.getNumVex(); i++) {
                for (int j = 0; j < graph.getNumVex(); j++) {
                    System.out.print(p[i][j] + " ");
                }
                System.out.println("\n");
            }
        }
    
        /**
         * 初始化p矩阵
         * 
         */
        private void initP() {
            for (int i = 0; i < graph.getNumVex(); i++) {
                for (int j = 0; j < graph.getNumVex(); j++) {
                    p[i][j] = j;
                }
            }
        }
    
        /**
         * 对d和p进行变化
         * 
         */
        private void work() {
            for (int k = 0; k < graph.getNumVex(); k++) {
                for (int v = 0; v < graph.getNumVex(); v++) {
                    for (int w = 0; w < graph.getNumVex(); w++) {
                        if (d[v][w] > d[v][k] + d[k][w]) {
                            d[v][w] = d[v][k] + d[k][w];
                            p[v][w] = p[v][k];// p[v][w]是v--w最短路径上 v的下一顶点
                        }
                    }
                }
            }
        }
    
        /**
         * 获取最短路劲
         * 
         */
        public void getShortestPath(int start, int end) {
            StringBuilder path = new StringBuilder();
            int index = start;// 起始点
            path.append(start + " → ");
    
            while (index != end) {
                // 循环取出路径上的各个顶点
                index = p[index][end];
                if (index != end) {
                    path.append(index + " →");
                }else {
                    path.append(index);
                }
    
            }
    
            System.out.println("从" + (start) + "到" + (end) + "的最短路径为"
                    + d[start][end] + "\n该路劲为:" + path.toString());
        }
    
        public static void getShortestPath(Floyd floyd) {
            Scanner scanner = new Scanner(System.in);
            System.out.println("求最短路径\n请输入起点:");
            int start = scanner.nextInt();
            System.out.println("请输入终点:");
            int end = scanner.nextInt();
            floyd.getShortestPath(start, end);
            System.out.println("是否继续计算其他最短路径 Y/N? ");
            String tag = scanner.next();
            if (tag.toLowerCase().equals("y")) {
                getShortestPath(floyd);
            }
    
        }
    
        /**
         * 图内部类
         * 
         * @author ccf
         * 
         */
        class Graph {
            /**
             * 定点数
             * 
             */
            private int numVex = 0;
            private int arc[][] = null;
            private int numEdge = 0;
            private final int INFINITY = 9999;
    
            public Graph() {
                System.out.print("请输入定点的数目:");
                Scanner scanner = new Scanner(System.in);
                this.numVex = scanner.nextInt();
                arc = new int[numVex][numVex];
                for (int i = 0; i < numVex; i++) {
                    for (int j = 0; j < numVex; j++) {
                        arc[i][j] = INFINITY;
                    }
                }
                for (int i = 0; i < numVex; i++) {
                    arc[i][i] = 0;
    
                }
                System.out.println("顶点数为:" + this.numVex);
                System.out.print("请输入边数:");
                scanner = new Scanner(System.in);
                this.numEdge = scanner.nextInt();
                System.out.println("边数为:" + this.numEdge);
    
                System.out.println("请输入(Vi,Vj)上下标i 和  j,以及权重,用逗号隔开");
                for (int i = 1; i <= numEdge; i++) {
                    scanner = new Scanner(System.in);
                    String a = scanner.nextLine();
                    String[] b = a.split(",");
                    // System.out
                    // .println("输入了:" + Integer.parseInt(b[0]) + " "
                    // + Integer.parseInt(b[1]) + " "
                    // + Integer.parseInt(b[2]));
                    arc[Integer.parseInt(b[0])][Integer.parseInt(b[1])] = Integer
                            .parseInt(b[2]);
                    arc[Integer.parseInt(b[1])][Integer.parseInt(b[0])] = Integer
                            .parseInt(b[2]);
    
                }
                
            }
    
            public int[][] getArc() {
                return arc;
            }
    
            public int getNumVex() {
                return numVex;
            }
    
        }
    
        public static void main(String[] args) {
            Floyd floyd = new Floyd();
            getShortestPath(floyd);
        }
    
    }
  • 相关阅读:
    LoadRunner12 Java Vuser API语法举例
    Java代码封装redis工具类
    Java代码redis基础操作
    Git提交代码失败: empty ident name (for <>) not allowed
    Ubuntu 16.04 root环境变量不生效问题解决方案
    Jenkins中使用GitLab的配置
    gitlab搭建与配置说明
    移动端网页开发经验总结 (不断更新ing)
    移动端开发注意事项(转载)
    电脑上调试手机网站的几种方法
  • 原文地址:https://www.cnblogs.com/chenchuangfeng/p/2989825.html
Copyright © 2020-2023  润新知