弗洛伊德(Floyd)算(F算法)— 最短寻径问题

1,应用场景—最短寻径问题

在这里插入图片描述

  • 弗洛伊德算法与迪杰斯特拉算法解决问题完全一致,这是解题思路不同

2,弗洛伊德算法介绍

  • 和迪杰斯特拉(Dijkstra)算法一样,弗洛伊德(Floyd)算法也是一种用于寻找加权图中顶点间最短路径的算法,该算法创始人为1978年图灵奖获得者罗比特 · 弗洛伊德
  • 与迪杰斯塔拉算法不同的是:迪杰斯特拉算法基于一个给定的出发顶点,求出该顶点到其他顶点的最短距离;弗洛伊德算法将每一个顶点作为出发顶点,所以需要求出每一个顶点到其他顶点的最短路径;
  • 迪杰斯特拉算法最后给出的结果是一个一维数组,即给定顶点到其他顶点的最短距离数组;弗洛伊德算法最后给出的结果是一个二维数组,即每一个顶点到其他顶点的最短距离数组

2.1,图解分析

  1. 弗洛伊德算法基本思想:设顶点vi到顶点vk的最短路径已知为Lik,顶点vk到顶点vj的最短路径已知为Lkj,那么vivj的最短路径距离为min((Lik + Lkj), Lij)vk的取值可能为所有顶点

  2. 弗洛伊德算法的代码呈现就是三层for循环,循环对象都是全部顶点

    • 第一层循环,中间顶点循环,确定中间顶点k
    • 第二层循环,出发顶点循环,确定出发顶点i
    • 第三层循环,结束顶点循环,确定结束顶点j
    • 以出发顶点i -> 中间顶点k -> 结束顶点j构成完成路径,然后参考第一步进行距离比较,如果符合替换标准,则对距离表和顶点前驱关系表进行更新
    • 上一步比较完成后,结束顶点递增
    • 结束顶点一轮递增完成后,出发顶点递增
    • 出发顶点递增完成后,中间顶点递增
    • 全部比较完成后, 则弗洛伊德算法完成
  3. 以图为例,对于应用场景中给出的无向图,初始化各顶点之间的距离表和前驱关系表后,此处前驱关系表在初始化时默认初始化为各自本身
    在这里插入图片描述

  4. 开始以A顶点作为中间顶点,进行第一轮的比较,比较完成后更新图如下:
    在这里插入图片描述

    • B -> C没有连通,所以通过A作为中间顶点,所以B -> C = A -> C + A -> B = 12,同理可知C -> G = 9,则距离表进行对应改变
    • 在前驱关系表中,B -> C因为有了中间顶点A的介入,则对应的前驱顶点应该改为A
  5. 进行以A之后的顶点分别作为中间顶点进行后续计算,直到全部执行完成

3,代码实现

package com.self.datastructure.algorithm.floyd;

import lombok.Getter;

import java.util.Arrays;

/**
 * 弗洛伊德算法
 * * 弗洛伊德算法其实就是三层for循环, 通过间接连接比较直接连接是否距离最短
 * * 三层for循环都是以全部顶点进行循环
 * * 第一层循环作为中间顶点循环
 * * 第二层循环作为出发顶点循环
 * * 第三层循环作为结束顶点循环
 * * 如果通过中间顶点过度后的三点距离, 小于出发顶点到结束顶点的距离,
 * * 则对距离表进行替换, 并更新前驱关系表的前驱顶点为当前遍历的中间顶点
 * * 第一层循环的全部顶点作为中间顶点遍历完成后, 则算法完成
 * @author pj_zhang
 * @create 2020-07-12 22:05
 **/
public class Floyd {

    private static final int NON = Integer.MAX_VALUE;

    public static void main(String[] args) {
        // 顶点列表
        char[] lstVertex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        // 顶点图, 该图中各自访问为0点
        int[][] vertexMap = {
                {0, 5, 7, NON, NON, NON, 2},
                {5, 0, NON, 9, NON, NON, 3},
                {7, NON, 0, NON, 8, NON, NON},
                {NON, 9, NON, 0, NON, 4, NON},
                {NON, NON, 8, NON, 0, 5, 4},
                {NON, NON, NON, 4, 5, 0, 6},
                {2, 3, NON, NON, 4, 6, 0}
        };
        MyGraph myGraph = new MyGraph(lstVertex, vertexMap);
        // 进行弗洛伊德计算
        floyd(myGraph);
        // 处理完成, 进行结果输出
        int[][] result = myGraph.getVertexMap();
        System.out.println("距离结果输出: ");
        for (int i = 0; i < result.length; i++) {
            System.out.println(Arrays.toString(result[i]));
        }
        char[][] preVertexArr = myGraph.getPreVertexArr();
        System.out.println("前驱顶点结果输出: ");
        System.out.println("    A  B  C  D  E  F  G ");
        for (int i = 0; i < preVertexArr.length; i++) {
            System.out.print(lstVertex[i] + ": ");
            System.out.println(Arrays.toString(preVertexArr[i]));
        }
    }

    /**
     * 弗洛伊德计算
     * @param myGraph
     */
    private static void floyd(MyGraph myGraph) {
        int[][] vertexMap = myGraph.getVertexMap();
        char[][] preVertexArr = myGraph.getPreVertexArr();
        char[] lstVertex = myGraph.getLstVertex();
        // 首先遍历中间顶点
        for (int middleIndex = 0; middleIndex < lstVertex.length; middleIndex++) {
            int[] middleDis = vertexMap[middleIndex];
            // 然后遍历起始顶点
            for (int startIndex = 0; startIndex < lstVertex.length; startIndex++) {
                // 如果顶点不连通, 直接进行下一轮循环
                if (NON == middleDis[startIndex]) {
                    continue;
                }
                // 最后遍历结束顶点
                for (int endIndex = 0; endIndex < lstVertex.length; endIndex++) {
                    // 顶点不连通, 直接进行下一轮循环
                    if (NON == middleDis[endIndex]) {
                        continue;
                    }
                    // 顶点连通的情况下进行后续处理
                    // 获取中间顶点到初始顶点的值
                    int startMidDis = vertexMap[middleIndex][startIndex];
                    // 获取中间顶点到到结束顶点的值
                    int endMidDis = vertexMap[middleIndex][endIndex];
                    // 获取初始顶点到结束顶点的值
                    int startEndDis = vertexMap[startIndex][endIndex];
                    // 如果通过中间顶点连接后, 距离小于直接连接的距离(未连接为极值)
                    // 则进行顶点值替换, 并将前驱顶点数组对应位置的前驱顶点改为中间顶点
                    if ((startMidDis + endMidDis) < startEndDis) {
                        vertexMap[startIndex][endIndex] = startMidDis + endMidDis;
                        vertexMap[endIndex][startIndex] = startMidDis + endMidDis;
                        preVertexArr[startIndex][endIndex] = lstVertex[middleIndex];
                        preVertexArr[endIndex][startIndex] = lstVertex[middleIndex];
                    }
                }
            }
        }
    }


    /**
     * 图表类
     */
    @Getter
    static class MyGraph {

        /**
         * 顶点数量
         */
        private int vertexCount;

        /**
         * 顶点列表
         */
        private char[] lstVertex;

        /**
         * 顶点连接图
         */
        private int[][] vertexMap;

        /**
         * 前驱顶点数组
         */
        private char[][] preVertexArr;

        public MyGraph(char[] lstVertex, int[][] vertexMap) {
            this.vertexCount = lstVertex.length;
            this.lstVertex = lstVertex;
            this.vertexMap = vertexMap;
            // 初始化前驱顶点数组
            // 将每一组顶点的前驱顶点先设置为自身
            preVertexArr = new char[this.vertexCount][this.vertexCount];
            for (int i = 0; i < preVertexArr.length; i++) {
                char[] currData = this.preVertexArr[i];
                for (int j = 0; j < currData.length; j++) {
                    preVertexArr[i][j] = lstVertex[i];
                }
            }
        }

    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值