【算法】最短路径——弗洛伊德 (Floyd) 算法

news2024/12/23 9:21:51

目录

  • 1.概述
  • 2.代码实现
  • 3.扩展
  • 3.应用

1.概述

(1)弗洛伊德 (Floyd) 算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与 Dijkstra 算法类似。该算法名称以创始人之一、1978 年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。

(2)弗洛伊德 (Floyd) 算法的具体思路如下:

  • 创建一个二维数组 dist,用于存储任意两个顶点之间的最短路径长度。
  • 初始化 dist 数组,将每条边的权重赋值给对应的 dist[i][j],其中 i 和 j 分别表示边的起点和终点。
  • 使用三重循环,分别遍历每一个顶点 k,将顶点 k 作为中间节点,更新 dist 数组。
  • 对于每一对顶点 i 和 j,如果 dist[i][j] 大于 dist[i][k] + dist[k][j],则更新 dist[i][j] 为 dist[i][k] + dist[k][j]。
  • 最终,dist 数组中存储的就是每对顶点之间的最短路径长度。

该算法的核心思想是通过动态规划逐步构建最短路径。通过逐渐扩展考虑的中间节点集合,可以确保得到最终的最短路径。通过多次迭代,每对顶点之间的最短路径逐渐更新,直到遍历完所有的中间节点,得到最后的最短路径矩阵。

(3)弗洛伊德 (Floyd) 算法的时间复杂度和空间复杂度分别如下:

  • 时间复杂度为 O(V3),弗洛伊德算法使用三重循环来迭代更新所有顶点对之间的最短路径,其中 V 表示图中顶点的数量。因此,算法的时间复杂度为 O(V3)。
  • 空间复杂度为 O(V2):算法需要使用一个二维数组来存储图中顶点对之间的最短路径距离,因此空间复杂度为 O(V2)。

有关 Dijkstra 算法的具体介绍可以参考【算法】最短路径——迪杰斯特拉 (Dijkstra) 算法这篇文章。

2.代码实现

(1)有关 Floyd 算法分别使用邻接矩阵和邻接表实现的说明如下:

  • Floyd 算法可以使用邻接表实现,但相比邻接矩阵,使用邻接表会增加一些复杂度,因为邻接表中存储了每个节点的出边信息,并没有存储到达该节点的边的信息。因此,在计算两个节点之间的距离时,需要遍历邻接表中连接这两个节点的所有边,这可能会使得算法的时间复杂度和空间复杂度都增加。另外,使用邻接表需要额外的开销来维护节点之间的关系。
  • 具体实现时,可以将每个节点的出边信息存储在一个链表、向量等动态数据结构中。为了能够以常数时间检查两个节点之间是否有边,可以使用哈希表来存储每个节点的出边信息。在遍历所有节点对时,对于每对节点 i 和 j,需要分别遍历它们的出边列表,以找到连接它们的路径。
  • 总之,虽然 Floyd 算法可以使用邻接表实现,但邻接矩阵更为简单和直观,并且在实现中运行时间更短和更节省空间。只有在图的规模比较大、稀疏程度比较高时,才考虑使用邻接表来表示图和实现弗洛伊德算法。

(2)下面给出 Floyd 算法的邻接矩阵实现:

class Solution {
    /*
         graph: 用于表示图的邻接矩阵
         返回值: 最短路径矩阵
    */
    public int[][] floyd(int[][] graph) {
        int n = graph.length;
        //初始化距离矩阵为邻接矩阵
        int[][] dist = new int[n][n];
        for (int i = 0; i < n; i++) {
            System.arraycopy(graph[i], 0, dist[i], 0, n);
        }
        //三重循环,依次计算每对节点之间的最短路径
        for (int k = 0; k < n; k++) {
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    if (dist[i][k] != Integer.MAX_VALUE && dist[k][j] != Integer.MAX_VALUE) {
                        //如果经过中间节点 k 更短,则更新路径长度
                        dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]);
                    }
                }
            }
        }
        return dist;
    }
}

(3)本测试中的加权无向图如下所示。
在这里插入图片描述

class Test {
	public static void main(String[] args) {
        //图的顶点数
        int n = 7;
        int[][] graph = new int[n][n];
        //初始化邻接矩阵,初始化为 Integer.MAX_VALUE 表示不可达
        for (int i = 0; i < n; i++) {
            Arrays.fill(graph[i], Integer.MAX_VALUE);
            graph[i][i] = 0;
        }
        //添加图的边
        graph[0][1] = 9;
        graph[0][5] = 1;
        graph[1][0] = 9;
        graph[1][2] = 4;
        graph[1][6] = 3;
        graph[2][1] = 4;
        graph[2][3] = 2;
        graph[3][2] = 2;
        graph[3][4] = 6;
        graph[3][6] = 5;
        graph[4][3] = 6;
        graph[4][5] = 8;
        graph[4][6] = 7;
        graph[5][0] = 1;
        graph[5][4] = 8;
        graph[6][1] = 3;
        graph[6][3] = 5;
        graph[6][4] = 7;

        Solution solution = new Solution();
        int[][] dist = solution.floyd(graph);
        //输出最短路径矩阵
        System.out.println("The shortest path matrix:");
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (dist[i][j] == Integer.MAX_VALUE) {
                    System.out.print("INF \t");
                } else {
                    System.out.print(dist[i][j] + " \t");
                }
            }
            System.out.println();
        }
    }
}

输出结果如下:

The shortest path matrix:
0 	9 	13 	15 	9 	1 	12 	
9 	0 	4 	6 	10 	10 	3 	
13 	4 	0 	2 	8 	14 	7 	
15 	6 	2 	0 	6 	14 	5 	
9 	10 	8 	6 	0 	8 	7 	
1 	10 	14 	14 	8 	0 	13 	
12 	3 	7 	5 	7 	13 	0 

3.扩展

(1)如果要求出每对顶点最短路径之间依次经过的节点,我们可以再增加一个矩阵来保存路径信息。设 paths[i][j] 表示节点i到节点j的最短路径经过的顶点序列。则 paths[i][j] 的计算如下:

  • 如果 dist[i][j] 为 INF(即节点 i 无法到达节点 j),则 paths[i][j] 为 null
  • 如果 i 和 j 之间有一条边(即 graph[i][j] 不为 INF),则 paths[i][j] 的值为 [i, j]
  • 否则,如果 dist[i][j] 可由 i 到达一个中间节点 k,然后由 k 到达 j 的路径更短,则在 paths[i][j] 中增加 k,并将 i->j 的最短路径更新为 i->k->j

(2)具体实现时,可以在弗洛伊德算法中增加一个与 dist 矩阵同样大小的 paths 矩阵,通过修改弗洛伊德算法中更新距离的语句,同时更新 paths 矩阵中的值,最终输出 paths 矩阵中的每个元素即可。具体实现如下所示:

class Solution {
    /*
         graph: 用于表示图的邻接矩阵
         返回值: 路径矩阵
    */
    public int[][] floydWithPath(int[][] graph) {
        int n = graph.length;
        //初始化距离矩阵和路径矩阵
        int[][] dist = new int[n][n];
        int[][] paths = new int[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                dist[i][j] = graph[i][j];
                if (graph[i][j] != Integer.MAX_VALUE) {
                    paths[i][j] = j;
                } else {
                    paths[i][j] = -1;
                }
            }
        }

        //三重循环,依次计算每对节点之间的最短路径
        for (int k = 0; k < n; k++) {
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    if (dist[i][k] != Integer.MAX_VALUE && dist[k][j] != Integer.MAX_VALUE) {
                        // 如果经过中间节点 k 更短,则更新路径长度和路径矩阵
                        if (dist[i][j] > dist[i][k] + dist[k][j]) {
                            dist[i][j] = dist[i][k] + dist[k][j];
                            paths[i][j] = paths[i][k];
                        }
                    }
                }
            }
        }
        return paths;
    }
}

(3)本测试中的加权无向图与上面一样,测试代码如下所示:

class Test {
	public static void main(String[] args) {
        //图的顶点数
        int n = 7;
        int[][] graph = new int[n][n];
        //初始化邻接矩阵,初始化为 Integer.MAX_VALUE 表示不可达
        for (int i = 0; i < n; i++) {
            Arrays.fill(graph[i], Integer.MAX_VALUE);
            graph[i][i] = 0;
        }
        //添加图的边
        graph[0][1] = 9;
        graph[0][5] = 1;
        graph[1][0] = 9;
        graph[1][2] = 4;
        graph[1][6] = 3;
        graph[2][1] = 4;
        graph[2][3] = 2;
        graph[3][2] = 2;
        graph[3][4] = 6;
        graph[3][6] = 5;
        graph[4][3] = 6;
        graph[4][5] = 8;
        graph[4][6] = 7;
        graph[5][0] = 1;
        graph[5][4] = 8;
        graph[6][1] = 3;
        graph[6][3] = 5;
        graph[6][4] = 7;

        Solution solution = new Solution();
        int[][] paths = solution.floydWithPath(graph);
        System.out.println("The path matrix:");
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (paths[i][j] == -1) {
                    System.out.print("null  ");
                } else {
                    List<Integer> p = new ArrayList<>();
                    int cur = i;
                    while (cur != j) {
                        p.add(cur);
                        cur = paths[cur][j];
                    }
                    p.add(j);
                    System.out.print(p + "  ");
                }
            }
            System.out.println();
        }
    }
}

输出结果如下:

The path matrix:
[0]  [0, 1]  [0, 1, 2]  [0, 1, 2, 3]  [0, 5, 4]  [0, 5]  [0, 1, 6]  
[1, 0]  [1]  [1, 2]  [1, 2, 3]  [1, 6, 4]  [1, 0, 5]  [1, 6]  
[2, 1, 0]  [2, 1]  [2]  [2, 3]  [2, 3, 4]  [2, 1, 0, 5]  [2, 1, 6]  
[3, 2, 1, 0]  [3, 2, 1]  [3, 2]  [3]  [3, 4]  [3, 4, 5]  [3, 6]  
[4, 5, 0]  [4, 6, 1]  [4, 3, 2]  [4, 3]  [4]  [4, 5]  [4, 6]  
[5, 0]  [5, 0, 1]  [5, 0, 1, 2]  [5, 4, 3]  [5, 4]  [5]  [5, 0, 1, 6]  
[6, 1, 0]  [6, 1]  [6, 1, 2]  [6, 3]  [6, 4]  [6, 1, 0, 5]  [6] 

3.应用

(1)弗洛伊德 (Floyd) 算法的应用场景包括但不限于以下几个方面:

  • 多源最短路径问题:弗洛伊德算法可以解决图中任意两个顶点之间的最短路径问题。这在路由算法、网络通信中非常有用,可以帮助确定从一个网络节点到其他节点的最短路径。
  • 公交网络规划:在城市公交系统中,弗洛伊德算法可以用于计算任意两个站点之间的最短路径,从而帮助规划公交线路和乘车路线。
  • 遗传算法中的距离计算:在遗传算法中,常常需要计算个体之间的距离或相似度,弗洛伊德算法可以用于计算从一个个体到其他个体之间的最短距离,以便进行后续的选择、交叉和变异操作。
  • 最优路径规划:弗洛伊德算法可以用于在地图导航系统中计算最短路径,帮助用户在城市道路网中找到最佳的行驶路径。
  • 交通流量优化:弗洛伊德算法可以用于计算交通网络中的最短路径,以优化交通流量分配,避免拥堵和优化道路资源利用。

(2)大家可以去 LeetCode 上找相关的 Floyd 算法的题目来练习,或者也可以直接查看 LeetCode 算法刷题目录 (Java) 这篇文章中的最短路径章节。如果大家发现文章中的错误之处,可在评论区中指出。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1223982.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Matalab插值详解和源码

转载&#xff1a;Matalab插值详解和源码 - 知乎 (zhihu.com) 插值法 插值法又称“内插法”&#xff0c;是利用函数f (x)在某区间中已知的若干点的函数值&#xff0c;作出适当的特定函数&#xff0c;在区间的其他点上用这特定函数的值作为函数f (x)的近似值&#xff0c;这种方…

腾讯云服务器租用价格,腾讯云服务器价格流量怎么算?

首先&#xff0c;让我们来看看腾讯云服务器租用价格。根据您的需求不同&#xff0c;腾讯云提供了多种不同的配置选项&#xff0c;从轻量级应用服务器到高性能的GPU服务器&#xff0c;都可以满足您的需求。以下是一些常见的腾讯云服务器租用价格&#xff1a; 一、腾讯云服务器租…

VPN创建连接 提示错误 628: 在连接完成前,连接被远程计算机终止。

提示错误 628: 在连接完成前&#xff0c;连接被远程计算机终止。 需要把这个地址配置一下&#xff1a; VPN类型&#xff1a;点对点PPTP 数据加密&#xff1a;如果没有加密的话&#xff0c; 要把这个选一下。

Flask 接口

目录 前言 代码实现 简单接口实现 执行其它程序接口 携带参数访问接口 前言 有时候会想着开个一个接口来访问试试&#xff0c;这里就给出一个基础接口代码示例 代码实现 导入Flask模块&#xff0c;没安装Flask 模块需要进行 安装&#xff1a;pip install flask 使用镜…

LeetCode——字符串(Java)

字符串 简介[简单] 344. 反转字符串[简单] 541. 反转字符串 II[中等] 151. 反转字符串中的单词 简介 记录一下自己刷题的历程以及代码。写题过程中参考了 代码随想录。会附上一些个人的思路&#xff0c;如果有错误&#xff0c;可以在评论区提醒一下。 [简单] 344. 反转字符串…

AIGC创作系统ChatGPT源码,AI绘画源码,支持最新GPT-4-Turbo模型,支持DALL-E3文生图

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…

课程设计(毕业设计)—基于机器学习(CNN+opencv+python)的车牌识别—(可远程调试)计算机专业课程设计(毕业设计)

基于机器学习(CNNopencvpython)的车牌识别 下载本文机器学习(CNNopencvpython)的车牌识别系统完整的代码和参考报告链接&#xff08;或者可以联系博主koukou(壹壹23七2五六98)&#xff0c;获取源码和报告&#xff09;https://download.csdn.net/download/shooter7/88548767此处…

操作系统·进程同步

进程同步&#xff1a;异步环境下的一组并发进程因直接制约而互相发送消息、互相合作、互相等待&#xff0c;使得各进程按照一定的速度执行的过程。 进程同步的主要任务是使并发执行的诸进程之间能有效地共享资源和相互合作&#xff0c;使执行的结果具有可再现性。 4.1 进程同…

智慧城市指挥中心,大屏幕究竟有什么用?

目前很多地区有在兴建智慧城市的项目&#xff0c;其城市指挥中心内一般都建有一张巨大的屏幕&#xff0c;这张屏幕究竟有什么用&#xff1f;是否可以用普通的电脑显示器进行代替呢&#xff1f; 智慧城市指挥中心内的巨大屏幕是智慧城市项目中的重要组成部分&#xff0c;其作用不…

​软考-高级-系统架构设计师教程(清华第2版)【第14章 云原生架构设计理论与实践(P496~526)-思维导图】​

软考-高级-系统架构设计师教程&#xff08;清华第2版&#xff09;【第14章 云原生架构设计理论与实践&#xff08;P496~526&#xff09;-思维导图】 课本里章节里所有蓝色字体的思维导图

Springboot+vue的应急物资管理系统(有报告),Javaee项目,springboot vue前后端分离项目

演示视频&#xff1a; Springbootvue的应急物资管理系统&#xff08;有报告&#xff09;&#xff0c;Javaee项目&#xff0c;springboot vue前后端分离项目 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。…

【GUI】-- 09 JComboBox JList、JTextField JPasswordField JTextArea

GUI编程 03 Swing 3.6 列表 下拉框 package com.duo.lesson06;import javax.swing.*; import java.awt.*;public class ComboBoxDemo01 extends JFrame {public ComboBoxDemo01() throws HeadlessException {Container contentPane getContentPane();JComboBox<Object&…

Flutter 3.16 中带来的更新

Flutter 3.16 中带来的更新 目 录 1. 概述2. 框架更新2.1 Material 3 成为新默认2.2 支持 Material 3 动画2.3 TextScaler2.4 SelectionArea 更新2.5 MatrixTransition 动画2.6 滚动更新2.7 在编辑菜单中添加附加选项2.8 PaintPattern 添加到 flutter_test 3. 引擎更新&#xf…

STM32 HAL库函数HAL_SPI_Receive_IT和HAL_SPI_Receive的区别

背景 前段时间开发一个按键板驱动&#xff0c;该板用的STM32F103系列单片机&#xff0c;前任工程师用STM32CubeMX生成的工程&#xff0c;里面全是HAL库调用&#xff0c;我接手后&#xff0c;学习了下HAL库的用法&#xff0c;踩坑不少&#xff0c;特别是带IT后缀的函数&#xf…

S7-1200PLC 作为MODBUSTCP服务器通信(多客户端访问)

S7-1200PLC作为MODBUSTCP服务器端通信编程应用&#xff0c;详细内容请查看下面文章链接&#xff1a; ModbusTcp通信(S7-1200PLC作为服务器端)-CSDN博客文章浏览阅读239次。S7-200Smart plc作为ModbusTcp服务器端的通信S7-200SMART PLC ModbusTCP通信(ModbusTcp服务器)_s7-200 …

基于MS16F3211芯片的触摸控制灯的状态变化和亮度控制(11.17,PWM控制与状态切换)

1.今天做了什么 2.过程思路 看了两天文档才慢慢看懂&#xff0c;有点满了 现在接着前一天的思路&#xff0c;可以通过代码来控制pwm的占空比。我这里采用的是TP0定时器 初步控制pwm的占空比 void LED_PWM_OPEN(void) {//占空比 PWM1-Y-PB2PWM1DH 0X0F;PWM1DL 0X00; //占…

python3:turtle绘图 .2023-11-18

绘制一个菱形:四边相等且都为200像素;四个内角两边各为60度,上下各为120度 import turtle #导入turtle #画笔默认绘制方向为水平向右 turtle.right(-30) #画笔绘制方向向左(逆时针)旋转30度. turtle.fd(200) #画笔沿绘制方向绘制200像素长度 turtle.right(60) #画笔绘制方向在…

OSS服务和MinIO存储做一个区分解析

日落金山&#xff0c;明天我们继续… 什么是OSS服务和MinIO存储 OSS&#xff08;Object Storage Service&#xff09;和MinIO都是对象存储服务&#xff0c;但它们有一些区别。以下是对它们的简要分析&#xff1a; 1. 部署和管理&#xff1a; OSS&#xff1a; 由阿里云提供&a…

HDMI之EDID析义篇

DisplayID Type X Video Timing Data Block 实例 F0 2A 10 93 FF 0E 6F 08 8F 10 93 7F 07 37 04 8F 10该数据来源于SHARP AQUOS-TVE23A 4K144Hz电视机的第3个EDID块(基于HF-EEODB)。 定义 解释 VTDB 1: 3840x2160 144.000009 Hz 16:9 333.216 kHz 1343.527000 MHz (RBv3,h…

UE 程序化网格 计算横截面

首先在构造函数内加上程序化网格&#xff0c;然后复制网格体到程序化网格组件上&#xff0c;将Static Mesh&#xff08;类型StaticMeshActor&#xff09;的静态网格体组件给到程序化网格体上 然后把StaticMesh&#xff08;类型为StaticMeshActor&#xff09;Instance暴漏出去 …