Floyd是咋求图的最短路径?(二)

joytrian
发布于 2022-7-6 17:01
浏览
0收藏

 

程序实现
而对于程序而言,这个插入的过程相当简单。核心代码只有四行! 这个写法适合有向图和无向图,无向图的算法优化后面会说。
代码如下

public class floyd {
    static int max = 66666;// 别Intege.max 两个相加越界为负
    public static void main(String[] args) {
        int dist[][] = {
                { 0, 2, 3, 6, max, max }, 
                { 2, 0, max, max,4, 6 }, 
                { 3, max, 0, 2, max, max },
                { 6, max, 2, 0, 1, 3 }, 
                { max, 4, max, 1, 0, max }, 
                { max, 6, max, 3, max, 0 } };// 地图
        // 6个
        for (int k = 0; k < 6; k++)// 加入第k个节点进行计算
        {
            for (int i = 0; i < 6; i++)// 每加入一个点都要枚举图看看有没有可以被更新的
            {
                for (int j = 0; j < 6; j++)
                {
                    dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]);
                }
            }
        }
        // 输出
        for (int i = 0; i < 6; i++) {
            System.out.print("节点"+(i+1)+" 的最短路径");
            for (int j = 0; j < 6; j++) {
                System.out.print(dist[i][j]+" ");
            }
            System.out.println();
        }
    }
}

执行结果为:

Floyd是咋求图的最短路径?(二)-鸿蒙开发者社区
 

可以自行计算,图和上篇的Dijkstra用的图是一致的,大家可以自行比对,结果一致,说明咱么的结果成功的。

当然,在你学习的过程中,可以在每加入一个节点插入完成后,打印邻接矩阵的结果,看看前两部和笔者的是否相同(有助于理解),如果相同,则说明正确!

对于加入点更新你可能还是有点疑惑其中的过程,那咱么就用一个局部来演示一下帮助你进一步理解Floyd算法,看其中AB最短距离变化情况祝你理解:

Floyd是咋求图的最短路径?(二)-鸿蒙开发者社区
 

小试牛刀
自己会没会?刷一道题就可以知道了,刚好力扣1334是一道Floyd算法解决的问题。

题目描述为:

有 n 个城市,按从 0 到 n-1 编号。给你一个边数组 edges,其中 edges[i] = [fromi, toi, weighti] 代表 fromi 和 toi 两个城市之间的双向加权边,距离阈值是一个整数 distanceThreshold。

返回能通过某些路径到达其他城市数目最少、且路径距离 最大 为 distanceThreshold 的城市。如果有多个这样的城市,则返回编号最大的城市。

注意,连接城市 i 和 j 的路径的距离等于沿该路径的所有边的权重之和。

示例1:

Floyd是咋求图的最短路径?(二)-鸿蒙开发者社区

输入:n = 4, edges = [[0,1,3],[1,2,1],[1,3,4],[2,3,1]], distanceThreshold = 4
输出:3
解释:城市分布图如上。
每个城市阈值距离 distanceThreshold = 4 内的邻居城市分别是:
城市 0 -> [城市 1, 城市 2]
城市 1 -> [城市 0, 城市 2, 城市 3]
城市 2 -> [城市 0, 城市 1, 城市 3]
城市 3 -> [城市 1, 城市 2]
城市 0 和 3 在阈值距离 4 以内都有 2 个邻居城市,但是我们必须返回城市 3,因为它的编号最大。


示例2:

Floyd是咋求图的最短路径?(二)-鸿蒙开发者社区

输入:n = 5, edges = [[0,1,2],[0,4,8],[1,2,3],[1,4,2],[2,3,1],[3,4,1]], distanceThreshold = 2
输出:0
解释:城市分布图如上。
每个城市阈值距离 distanceThreshold = 2 内的邻居城市分别是:
城市 0 -> [城市 1]
城市 1 -> [城市 0, 城市 4]
城市 2 -> [城市 3, 城市 4]
城市 3 -> [城市 2, 城市 4]
城市 4 -> [城市 1, 城市 2, 城市 3]
城市 0 在阈值距离 2 以内只有 1 个邻居城市。


提示:

2 <= n <= 100
1 <= edges.length <= n * (n - 1) / 2
edges[i].length == 3
0 <= fromi < toi < n
1 <= weighti, distanceThreshold <= 10^4
所有 (fromi, toi) 都是不同的。


思路分析:

拿到一道题,首先就是要理解题意,而这道题的意思借助案例也是非常能够理解,其实就是判断在distanceThreshold范围内找到能够到达的最少点的编号,如果多个取最大即可。正常求到达最多情景比较多这里求的是最少的,但是思路都是一样的。

这道题如果使用搜索,那复杂度就太高了啊,很明显要使用多源最短路径Floyd算法,具体思路为;

1 .先使用Floyd算法求出点点之间的最短距离,时间复杂度O(n3)

2 . 统计每个点与其他点距离在distanceThreshold之内的点数量,统计的同时看看是不是小于等于已知最少个数的,如果是,那么保存更新。

3 .返回最终的结果。

实现代码:

class Solution {
    public int findTheCity(int n, int[][] edges, int distanceThreshold) {
        int dist[][]=new int[n][n];
        for(int i=0;i<n;i++) {
            for(int j=0;j<n;j++){
              //保证数据比最大二倍大(两相加不能比它大),并且不能溢出,不要Int最大 相加为负会出错
                dist[i][j]=1000000;
            }
            dist[i][i]=0;
        }
        for(int arr[]:edges){
            dist[arr[0]][arr[1]]=arr[2];
            dist[arr[1]][arr[0]]=arr[2];
        }
        for(int k=0;k<n;k++){
            for(int i=0;i<n;i++) {
                for(int j=0;j<n;j++){
                    dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]);
                }
            }
        }
        int min=Integer.MAX_VALUE;
        int minIndex=0;
        int pathNum[]=new int[n];//存储距离
        for(int i=0;i<n;i++) {
            for(int j=0;j<n;j++) {
                if(dist[i][j]<=distanceThreshold){
                    pathNum[i]++;
                }
            }
            if(pathNum[i]<=min) {
                min = pathNum[i];
                minIndex=i;
            }
        }
        return  minIndex;

    }
}

那么想一下优化空间:Floyd算法还有优化空间嘛?

有的,这个是个无向图,也就是加入点的时候枚举其实会有一个重复的操作过程(例如枚举AC和CA是效果一致的),所以我们在Floyd算法的实现过程中过滤掉重复的操作,具体代码为:

class Solution {
    public int findTheCity(int n, int[][] edges, int distanceThreshold) {
        int dist[][]=new int[n][n];//存储距离
        for(int i=0;i<n;i++) {
            for(int j=0;j<n;j++){
                dist[i][j]=1000000;
            }
            dist[i][i]=0;
        }
        for(int arr[]:edges){
            dist[arr[0]][arr[1]]=arr[2];
            dist[arr[1]][arr[0]]=arr[2];
        }
         for(int k=0;k<n;k++){
            for(int i=0;i<n;i++) {
                for(int j=i+1;j<n;j++){//去掉重复的计算
                    dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]);
                    dist[j][i]=dist[i][j];
                }
            }
        }
        int min=Integer.MAX_VALUE;
        int minIndex=0;
        int pathNum[]=new int[n];//
        for(int i=0;i<n;i++) {
            for(int j=0;j<n;j++) {
                if(dist[i][j]<=distanceThreshold){
                    pathNum[i]++;
                }
            }
            if(pathNum[i]<=min) {
                min = pathNum[i];
                minIndex=i;
            }
        }
        return  minIndex;

    }
}

尾声
对于Floyd算法,如果初次接触不一定能够理解这个松弛的过程。

Floyd像什么呢,最终最短路径大部分都是通过计算得到而存储下来直接使用的,我觉得它和MySQL视图有点像的,视图是一个虚表在实表上计算获得的,但是计算之后各个数据就可以直接使用,Floyd是在原本的路径图中通过一个动态规划的策略计算出来点点之间的最短路径。

Floyd和Dijkstra是经典的最短路径算法,两者有相似也有不同。在复杂度上,Dijkstra算法时间复杂度是O(n2),Floyd算法时间复杂度是O(n3);在功能上,Dijkstra是求单源最短路径,并且路径权值不能为负,而Floyd是求多源最短路径,可以有负权值;算法实现上,Dijkstra 是一种贪心算法实现起来较复杂,Floyd基于动态规划实现简单;两个作者Dijkstra和Floyd都是牛逼轰轰的大人物,都是图灵奖的获得者。

除了Floyd算法,堆排序算法heapSort也是Floyd大佬发明的,属实佩服!

Floyd算法,俗称插点法,不就一个点一个点插进去更新用到被插点距离嘛!

好啦,Floyd算法就介绍到这里,如果对你有帮助,请动动小手点个赞吧!蟹蟹。

 

文章转自公众号:bigsai

标签
已于2022-7-6 17:01:57修改
收藏
回复
举报
回复
    相关推荐