【数据结构与算法】图 Floyd算法

相关题目: 

1334. 阈值距离内邻居最少的城市 - 力扣(LeetCode)

资料 : 

Floyd算法原理及公式推导 - 知乎

Floyd 算法是一种经典的动态规划算法,用与求解图中所有顶点之间的最短短路路径。它由Robert Floyd  于1962 年提出,核心思想是通过“中间顶点”逐步松弛路径长度,最终得到任意两点间的最短距离。

算法核心 : 

图的类型:无向图 ,有向图(注意边的方向性)

边权特性: 支持负权边,但是不允许“包含负权边的回路”,否则会导致路径长度无线减小,无法收敛。

图的存储:  

使用邻接矩阵存储,用于快速访问任意两点间的边权。

算法原理步骤

动态规划 状态定义  

定义 dp[k][i][j]=表示只允许使用前k个节点作为中间节点 ,顶点 i 到顶点j 的最短距离。

根据状态转移: 

不选择第k个节点 作为中间节点:

dp[k][i][j]=dp[k-1][i][j]

选择第k个顶点组委中间节点:路径拆分为i->k->j

dp[k][i][j] =dp[k-1][i][j]+dp[k-1][i][j].

状态转移方程为:

dp[k][i][j]=min(dp[k-1][i][j],dp[k-1][i][k]+dp[k-1][k][j])

空间优化(关键)

观察dp[k][...]   仅依赖dp[k-1][...]  ,因此无需存储所有k 层状态,可以直接用二维数组dist覆盖更新

(k 作为外层循环,每次更新dist[i]j[ 时复用之前的结果])。

实现步骤

假设  图中有n个顶点,邻接矩阵初始化为dist ,步骤如下: 

初始化邻接矩阵

对顶点i, dist[i][i]=0  自身到自身的距离为0 

如果顶点i和j 直接有直接的边,权重为w 则dist[i][j]=w

如果顶点i 和j 无直接边,dist[i][j]=\infty , 表示不可达,通常用一个极大值如10^9

外层循环(枚举中间节点k)

遍历所有可能的中间顶点k(从1到n)

中间循环: 枚举起点i:

 遍历所有可能的起点i (从1到n)

内侧循环:枚举终点 j

遍历所有可能的终点j , (1到 n ),执行松弛操作,

dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])

(可选)检测负权回路

算法结束后,若存在 (dist[i][i] < 0),则说明图中存在包含顶点 i 的负权回路(因为自身到自身的距离不可能为负)

代码 

c++

#include <iostream>
#include <vector>
#include <climits>
using namespace std;const int INF = INT_MAX / 2;  // 避免加法溢出int main() {int n, m;  // n: 顶点数, m: 边数cin >> n >> m;// 1. 初始化邻接矩阵vector<vector<int>> dist(n + 1, vector<int>(n + 1, INF));for (int i = 1; i <= n; ++i) {dist[i][i] = 0;  // 自身到自身距离为0}// 读入边:u -> v,权值 wfor (int i = 0; i < m; ++i) {int u, v, w;cin >> u >> v >> w;dist[u][v] = w;  // 若为无向图,需额外添加 dist[v][u] = w}// 2. Floyd 算法核心(k: 中间顶点,i: 起点,j: 终点)for (int k = 1; k <= n; ++k) {for (int i = 1; i <= n; ++i) {for (int j = 1; j <= n; ++j) {// 若 i->k 或 k->j 不可达,则跳过(避免 INF + INF 溢出)if (dist[i][k] != INF && dist[k][j] != INF) {dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);}}}}// 3. 输出所有顶点间的最短距离cout << "所有顶点间的最短距离:" << endl;for (int i = 1; i <= n; ++i) {for (int j = 1; j <= n; ++j) {if (dist[i][j] == INF) {cout << "INF ";  // 不可达} else {cout << dist[i][j] << " ";}}cout << endl;}// 4. 检测负权回路bool has_negative_cycle = false;for (int i = 1; i <= n; ++i) {if (dist[i][i] < 0) {has_negative_cycle = true;break;}}if (has_negative_cycle) {cout << "图中存在负权回路!" << endl;}return 0;
}

python 

import sys
# 避免类似 C++ 中 INT_MAX 溢出问题,使用一个足够大的数表示“不可达”
INF = float('inf')def main():# 读取输入(顶点数 n,边数 m)# 注:Python 中 input() 读取单行,需拆分字符串获取整数;若输入较大可改用 sys.stdin 提速n, m = map(int, sys.stdin.readline().split())# 1. 初始化邻接矩阵:dist[i][j] 表示顶点 i 到 j 的初始距离# 顶点编号从 1 开始(与 C++ 保持一致,避免索引偏移)dist = [[INF] * (n + 1) for _ in range(n + 1)]# 自身到自身的距离为 0for i in range(1, n + 1):dist[i][i] = 0# 读入 m 条边:u -> v,权值 w(有向边)for _ in range(m):u, v, w = map(int, sys.stdin.readline().split())# 若存在多条从 u 到 v 的边,保留权值最小的一条(与原 C++ 逻辑一致)dist[u][v] = w# 【若为无向图】需添加以下一行(无向边等价于双向有向边):# dist[v][u] = w# 2. Floyd 算法核心:三层循环枚举中间顶点、起点、终点# k:中间顶点(允许经过前 k 个顶点时更新最短路径)for k in range(1, n + 1):# i:路径起点for i in range(1, n + 1):# j:路径终点for j in range(1, n + 1):# 跳过不可达的情况(避免 INF + INF 无意义计算)if dist[i][k] != INF and dist[k][j] != INF:# 松弛操作:更新 i->j 的最短距离if dist[i][j] > dist[i][k] + dist[k][j]:dist[i][j] = dist[i][k] + dist[k][j]# 3. 输出所有顶点间的最短距离(格式与 C++ 一致)print("所有顶点间的最短距离:")for i in range(1, n + 1):row = []for j in range(1, n + 1):if dist[i][j] == INF:row.append("INF")else:row.append(str(dist[i][j]))# 用空格连接一行的元素,与 C++ 输出格式对齐print(' '.join(row))# 4. 检测负权回路:若存在顶点 i 满足 dist[i][i] < 0,说明有负权回路has_negative_cycle = Falsefor i in range(1, n + 1):if dist[i][i] < 0:has_negative_cycle = Truebreakif has_negative_cycle:print("图中存在负权回路!")if __name__ == "__main__":main()

不可达值(INF): Python 中用 float('inf') 表示 “无穷大”,比 C++ 的 INT_MAX/2 更直观,且天然避免整数溢出问题(无需担心 INF + INF 超出范围)。

邻接矩阵初始化: 用 Python 列表推导式 [[INF]*(n+1) for _ in range(n+1)] 创建 (n+1)×(n+1) 的矩阵(顶点编号从 1 开始,与 C++ 一致,减少逻辑偏移)。

输入处理: 使用 sys.stdin.readline() 替代 input(),处理大量输入时效率更高(适配题目中可能的大规模图数据);通过 map(int, ...split()) 拆分输入字符串为整数,与 C++ 的 cin >> 逻辑一致。 核心松弛操作: 保留三层循环的顺序(k→i→j),仅将 C++ 的 min() 函数替换为 Python 的条件判断(if dist[i][j] > ...),逻辑完全等价,且更符合 Python 代码习惯。

输出格式: 用列表 row 收集每行的输出内容,最后通过 ' '.join(row) 用空格连接,确保输出格式与 C++ 一致(如 “INF” 对应不可达,数字对应最短距离)。 负权回路检测: 保留原逻辑 —— 遍历所有顶点 i,若 dist[i][i] < 0,说明存在经过 i 的负权回路(因为 “自身到自身的最短路径” 不可能为负)。

示例 

示例1

输入(3 个顶点,4 条有向边):

plaintext

3 4
1 2 2
1 3 6
2 3 1
3 1 -4

输出:

所有顶点间的最短距离: 0 2 3 -3 0 1 -4 -2 0 图中存在负权回路!

该示例中,dist[1][1] = 0、dist[2][2] = 0、dist[3][3] = 0 看似无负,但实际计算过程中会发现循环 1→2→3→1 的总权值为 2+1+(-4) = -1 < 0,最终代码会正确检测出负权回路。

示例2

无负权边的有向图示例

中间顶点如何优化最短路径

示例场景:4 个顶点的有向图

假设我们有一个包含 4 个顶点(编号 1~4)的有向图,边的连接和权值如下(边的方向和权重是核心):

  • 1 → 2,权值 3
  • 1 → 3,权值 6
  • 2 → 3,权值 2
  • 2 → 4,权值 5
  • 3 → 4,权值 1
  • 4 没有出边

第一步:明确输入格式

根据代码的输入要求(先输入顶点数 n 和边数 m,再输入 m 条边的起点、终点、权值),该示例的输入如下:

plaintext

4 5
1 2 3
1 3 6
2 3 2
2 4 5
3 4 1

第二步:算法执行逻辑拆解(核心步骤)

我们会按 Floyd 算法的三层循环(中间顶点 k → 起点 i → 终点 j),逐步展示邻接矩阵 dist 的更新过程,理解 “中间顶点如何缩短路径”。

1. 初始化邻接矩阵

初始时,dist[i][j] 的规则:

  • 自身到自身:dist[i][i] = 0
  • 有直接边:dist[i][j] = 边权
  • 无直接边:dist[i][j] = INF(用  表示)

初始 dist 矩阵(行是起点,列是终点):

起点 \ 终点1234
1036
2025
301
40

2. 枚举中间顶点 k=1(允许经过顶点 1 优化路径)

此时检查所有 i→j,看是否能通过 i→1→j 缩短路径。 由于顶点 1 的出边只有 1→2、1→3,且大部分 i→1 不可达(如 i=2,3,4 到 1 是 ∞),因此矩阵无更新。

3. 枚举中间顶点 k=2(允许经过顶点 2 优化路径)

检查所有 i→j,看是否能通过 i→2→j 缩短路径,关键更新如下:

  • 对于 i=1, j=3:原路径 1→3 权值 6;新路径 1→2→3 权值 3+2=5 → 更新 dist[1][3] = 5
  • 对于 i=1, j=4:原路径 1→4 是 ;新路径 1→2→4 权值 3+5=8 → 更新 dist[1][4] = 8

更新后的 dist 矩阵:

起点 \ 终点1234
10358
2025
301
40
4. 枚举中间顶点 k=3(允许经过顶点 3 优化路径)

检查所有 i→j,看是否能通过 i→3→j 缩短路径,关键更新如下:

  • 对于 i=1, j=4:原路径 1→4 权值 8;新路径 1→3→4 权值 5+1=6 → 更新 dist[1][4] = 6
  • 对于 i=2, j=4:原路径 2→4 权值 5;新路径 2→3→4 权值 2+1=3 → 更新 dist[2][4] = 3

更新后的 dist 矩阵:

起点 \ 终点1234
10356
2023
301
40
5. 枚举中间顶点 k=4(允许经过顶点 4 优化路径)

由于顶点 4 没有出边(所有 4→j 除了自身都是 ),因此矩阵无更新

第三步:代码输出结果

将上述输入代入 Python 代码,最终输出如下(包含所有顶点间的最短距离,且无负权回路):

plaintext

所有顶点间的最短距离:
0 3 5 6
INF 0 2 3
INF INF 0 1
INF INF INF 0

结果解读

输出矩阵的每一行代表 “从当前起点到所有终点的最短距离”:

  • 起点 1 到各终点:1→1(0)、1→2(3)、1→3(5,1→2→3)、1→4(6,1→2→3→4)
  • 起点 2 到各终点:2→1(不可达)、2→2(0)、2→3(2)、2→4(3,2→3→4)
  • 起点 3 到各终点:3→1/2(不可达)、3→3(0)、3→4(1)
  • 起点 4 到各终点:4→1/2/3(不可达)、4→4(0)

这与我们手动拆解的 “最优路径” 完全一致,验证了代码的正确性。

额外测试:含负权边(无负权回路) 若在上述示例中添加一条边 3→1,权值 -2(无负权回路),输入变为: plaintext 4 6 1 2 3 1 3 6 2 3 2 2 4 5 3 4 1 3 1 -2 代码会检测到 “无负权回路”,且更新后的 dist[2][1] 会变为 2→3→1 的权值 2 + (-2) = 0,dist[3][1] = -2 等,进一步体现 Floyd 算法对负权边的支持。

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

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

相关文章

卫星通信天线的指向精度,含义、测量和计算

卫星通信天线的指向精度&#xff0c;含义、测量和计算我们在卫星通信天线的技术规格书中&#xff0c;都会看到天线指向精度这个指标。一般来说&#xff0c;技术规格书上的天线指向精度的参数是这么写的&#xff1a;“天线指向精度≤1/10半功率波束带宽”今天这个文章&#xff0…

基于LSTM与3秒级Tick数据的金融时间序列预测实现

数据加载模块解析 def load_data(filepath):df pd.read_csv(filepath)return df该函数承担基础数据采集职责&#xff0c;通过Pandas库读取CSV格式的高频交易数据&#xff08;典型如股票分笔成交明细&#xff09;。输入参数为文件路径字符串&#xff0c;输出结构化DataFrame对象…

C# --- Field and Property

C# --- Field and Property字段 (Field) vs. 属性 (Property)Property的声明初始化方法单例类property错误初始化导致线程泄漏字段 (Field) vs. 属性 (Property) 字段 (Field) - 数据的存储容器 字段是直接在类或结构中声明的变量。它是存储数据的地方&#xff0c;是对象状态的…

【Python】实现一个文件夹快照与比较工具

1. 工具简介 在日常开发、项目管理或备份场景中&#xff0c;我们经常需要知道某个文件夹中的文件是否发生变化&#xff0c;例如&#xff1a; 项目源码是否新增或修改文件&#xff1f;数据集是否被不小心删除或篡改&#xff1f;备份文件夹是否和上次一致&#xff1f; 本教程将教…

LINUX913 shell:set ip [lindex $argv 0],\r,send_user,spawn ssh root@ip “cat “

问题 获取公钥 [codesamba ~]$ cat pub.sh #!/bin/usr/expect set ip "$1" set password 123456 set timeout 20 spawn ssh root192.168.235.100:cat ~/.ssh/id_rsa.pub expect { "yes/no" {send "yes/r";exp_continue} "password:" {…

Acwing算法基础课--链表

一、单链表 AcWing 826. 单链表 代码 N 100010 idx 0 e [0] * N ne [0] * N head -1def init():global idx,headidx 0head -1def add_head(x):global idx,heade[idx] xne[idx] headhead idxidx 1def delete(k):ne[k] ne[ne[k]]def add_k(k,x):global idxe[idx] …

AI表征了西方的有界,AI+体现了东方的无界

AI表征了西方的有界&#xff0c;AI体现了东方的无界&#xff0c;试图通过文化差异的视角来对比传统AI&#xff08;AI&#xff09;与增强型或融合型AI&#xff08;AI&#xff09;的特征。一、“AI表征了西方的有界”西方的“有界”可以理解为&#xff1a;1、逻辑清晰、结构严谨&…

LabVIEW泵轮检测

​在现代制造业蓬勃发展的浪潮下&#xff0c;汽车行业也迎来了高速发展期。液力变矩器作为实现车辆自动变速的关键零件产品&#xff0c;在汽车动力系统中扮演着不可或缺的角色。泵轮作为液力变矩器的核心组成部分&#xff0c;其生产质量直接影响着液力变矩器的性能。因此&#…

RT-DETRv2 中的坐标回归机制深度解析:为什么用 `sigmoid(inv_sigmoid(ref) + delta)` 而不是除以图像尺寸?

引言&#xff1a;一个看似简单的公式&#xff0c;背后藏着工业级设计智慧 在阅读 RT-DETRv2&#xff08;Real-Time DETR v2&#xff09;源码时&#xff0c;我曾被一行代码深深震撼&#xff1a; inter_ref_bbox F.sigmoid(bbox_head[i](output) inverse_sigmoid(ref_points_de…

简单了解一下GraphRAG

传统RAG的缺点 当我们将一段文本信息以句子分割后&#xff0c;存入到向量数据库中。用户提问“老王喜欢吃什么”&#xff0c;这个问题会与向量数据库中的许多句子关联性比较强&#xff0c;能返回准确且具体的信息。 但是&#xff0c;若是问题换成“出现了几次西瓜”&#xff0c…

HTTP 状态码背后的逻辑:从请求到响应的完整流程解析(含完整流程图)

在日常的 Web 开发与 API 调试中&#xff0c;我们经常会遇到各种 HTTP 状态码 ——404 Not Found、401 Unauthorized、500 Internal Server Error... 这些数字背后并非随机出现&#xff0c;而是服务器处理请求过程中不同阶段的 "反馈信号"。理解这些状态码的触发逻辑…

Vue:下拉框多选影响行高

目录 一、 出现场景二、 解决方案 一、 出现场景 在使用el-select增加multiple属性进行多选时&#xff0c;会出现高度塌陷的情况 二、 解决方案 首先需要在el-select中增加collapse-tags属性&#xff0c;并在style中增加如下样式 方案一 <style scoped> ::v-deep .e…

如何在高通跃龙QCS6490 Arm架构上使用Windows 11 IoT企业版?

1.简介研华已将高通跃龙QCS6490 技术应用于嵌入式模块、单板电脑和AI摄像头等各种规格的嵌入式硬件中。QCS6490平台支持全面的操作系统生态系统&#xff0c;包括Windows、Ubuntu、Yocto和 Android。Windows 11 IoT企业版是微软新一代的物联网操作系统&#xff0c;具有更强的安全…

阿里云国际代理:如何利用RDS构建高可用、可扩展的数据库架构

讲下云数据库RDS案例解析&#xff0c;若在上云或用云过程中有不懂的&#xff0c;可寻云枢国际yunshuguoji助力免卡上云用云。1、RDS MySQL数据库代理支持读写分离、连接保持、就近访问、事务拆分、连接池、SSL加密等功能&#xff0c;能够降低主实例负载&#xff0c;提高实例可用…

C++之特殊类设计

文章目录前言一、 设计一个不能被拷贝的类1. C98 实现方式2. C11 实现方式二、设计一个只能在堆上创建对象的类1. 方法一&#xff1a;析构函数私有&#xff0c;提供destory接口释放资源2. 方法二&#xff1a;构造函数私有三、 设计一个只能在栈上创建对象的类1. 实现方式四、设…

TupiTube,一款免费开源的 2D 动画创作工具

TupiTube&#xff0c;一款免费开源的 2D 动画创作工具 ** ** 功能 ** &#xff1a;开源、免费的 2D 动画软件&#xff0c;界面简单&#xff0c;支持逐帧动画、剪纸动画、定格动画&#xff0c;能导入素材并导出多种视频和图片格式&#xff0c;适合儿童、学生和动画爱好者入门创作…

MoE架构训练系统设计:专家并行与门控网络优化策略

点击 “AladdinEdu&#xff0c;同学们用得起的【H卡】算力平台”&#xff0c;注册即送-H卡级别算力&#xff0c;80G大显存&#xff0c;按量计费&#xff0c;灵活弹性&#xff0c;顶级配置&#xff0c;学生更享专属优惠。 摘要 混合专家&#xff08;Mixture of Experts&#xf…

使用Python爬虫,selenium和requests谁更强?

py爬虫的话&#xff0c;selenium和reqeusts谁更强&#xff0c;selenium是不是能完全取代requests? 答案基本是可以的&#xff0c;selenium适合动态网页抓取&#xff0c;因为它可以控制浏览器去点击、加载网页&#xff0c;requests则比较适合静态网页采集&#xff0c;它非常轻…

编译原理-文法压缩练习

这个任务的目标就是把一个给定的文法变得“干净”和“高效”&#xff0c;剔除所有无用的部分。根据幻灯片&#xff0c;无用的&#xff08;多余的&#xff09;规则分为两大类&#xff1a; 不可达规则&#xff1a;规则的“头”&#xff08;左部非终结符&#xff09;从起始符号出发…

GPU硬件架构和配置的理解

从公司架构理解GPU架构想象一个GPU就像一家大型科技公司&#xff0c;它的任务是处理图形和计算任务&#xff08;“干活”&#xff09;。硬件概念公司架构比喻作用和特点Platform (平台)集团公司最大的独立实体。比如谷歌Alphabet是一个集团公司&#xff0c;它旗下有谷歌、Waymo…