弗洛伊德(Floyd)算法
最短路径问题:从某个顶点出发到达另外一个顶点的所经过的边的权重和最小的一条路径
弗洛伊德算法
1、应用场景
(1)既适用于无向加权图,也适用于有向加权图
(2)使用弗洛伊德算法查找最短路径时,只允许环路的权值为负数,其它路径的权值必须为非负数,否则算法执行过程会出错
(3)基于动态规划算法实现
2、实现思路
(1)有向加权图
(2)建立一张表格,记录每个顶点直达其它所有顶点的权值,起始顶点指的是从哪个顶点出发,目标顶点指的是要达到的顶点,例如 2 -> 1 路径的权值是 2,顶点 2 是起始顶点,顶点 1 是目标顶点。此外,∞ 表示无穷大的数,即顶点之间不存在直达的路径
目标顶点 | |||||
1 | 2 | 3 | 4 | ||
起始顶点 | 1 | 0 | 3 | ∞ | 5 |
2 | 2 | 0 | ∞ | 4 | |
3 | ∞ | 1 | 0 | ∞ | |
4 | ∞ | ∞ | 2 | 0 |
(3)在表 1 的基础上,将顶点 1 作为 "中间顶点",计算从各个顶点出发途径顶点 1 再到达其它顶点的权值,如果比表 1 中记录的权值更小,证明两个顶点之间存在更短的路径,对表 1 进行更新
2-1-3:权值为 2 + ∞ = ∞,表 1 中记录的 2-3 的权值也是 ∞
2-1-4:权值为 2 + 5 = 7,表 1 中记录的 2-4 的权值是 4
3-1-2:权值为 ∞ + 3,表 1 中记录的 3-2 的权值是 1
3-1-4:权值为 ∞ + 5,表 1 中记录的 3-4 的权值是 ∞
4-1-2:权值为 ∞ + 3,表 1 中记录的 4-2 的权值是 ∞
4-1-3:权值为 ∞ + ∞,表 1 中记录的 4-3 的权值是 2
(4)以上所有的路径中,没有比表 1 中记录的权值最小的路径,所以不需要对表 1 进行更新
(5)在表 1 的基础上,以顶点 2 作为 "中间顶点",计算从各个顶点出发途径顶点 2 再到达其它顶点的权值
1-2-3:权值为 3 + ∞,表 1 中记录的 1-3 的权值为 ∞;
1-2-4:权值为 3 + 4 = 7,表 1 中 1-4 的权值为 5;
3-2-1:权值为 1 + 2 = 3,表 1 中 3-1 的权值为 ∞,3 < ∞;
3-2-4:权值为 1 + 4 = 5,表 1 中 3-4 的权值为 ∞,5 < ∞;
4-2-1:权值为 ∞ + 2,表 1 中 4-1 的权值为 ∞;
4-2-3:权值为 ∞ + ∞,表 1 中 4-3 的权值为 2。
(6)以顶点 2 作为 "中间顶点",找到了比 3-1、3-4 更短的路径,对表 1 进行更新
目标顶点 | |||||
1 | 2 | 3 | 4 | ||
起始顶点 | 1 | 0 | 3 | ∞ | 5 |
2 | 2 | 0 | ∞ | 4 | |
3 | 3(3-2-1) | 1 | 0 | 5(3-2-4) | |
4 | ∞ | ∞ | 2 | 0 |
(7)在表 2 的基础上,将顶点 3 作为 "中间顶点",计算从各个顶点出发途径顶点 3 再到达其它顶点的权值
1-3-2 权值为 ∞ + 1,表 2 中 1-2 的权值为 3;
1-3-4 权值为 ∞ + 5,表 2 中 1-4 的权值为 5;
2-3-1 权值为 ∞ + 3,表 2 中 2-1 的权值为 2;
2-3-4 权值为 ∞ + 5,表 2 中 2-4 的权值为 4;
4-3-1 权值为 2 + 3 = 5,表 2 中 4-1 的权值为 ∞,5 < ∞;
4-3-2 权值为 2 + 1 = 3,表 2 中 4-2 的权值为 ∞,3 < ∞;
(8)以顶点 3 作为 "中间顶点",我们找到了比 4-1、4-2 更短的路径,对表 2 进行更新
目标顶点 | |||||
1 | 2 | 3 | 4 | ||
起始顶点 | 1 | 0 | 3 | ∞ | 5 |
2 | 2 | 0 | ∞ | 4 | |
3 | 3(3-2-1) | 1 | 0 | 5(3-2-4) | |
4 | 5(4-3-2-1) | 3(4-3-2) | 2 | 0 |
(9)在表 3 的基础上,将顶点 4 作为 "中间顶点",计算从各个顶点出发途径顶点 4 再到达其它顶点的权值
1-4-2 权值为 5 + 3 = 8,表 3 中 1-2 的权值为 3
1-4-3 权值为 5 + 2 = 7,表 3 中 1-3 的权值为 ∞,7 < ∞
2-4-1 权值为 4 + 5 = 9,表 3 中 2-1 的权值为 2
2-4-3 权值为 4 + 2 = 6,表 3 中 2-3 的权值为 ∞,6 < ∞
3-4-1 权值为 4 + 5 = 9,表 3 中 3-1 的权值为 3
3-4-2 权值为 5 + 5 = 10 ,表 3 中 3-2 的权值为 1
(10)以顶点 4 作为 "中间顶点",我们找到了比 1-3、2-3 更短的路径,对表 3 进行更新
目标顶点 | |||||
1 | 2 | 3 | 4 | ||
起始顶点 | 1 | 0 | 3 | 7(1-4-3) | 5 |
2 | 2 | 0 | 6(2-4-3) | 4 | |
3 | 3(3-2-1) | 1 | 0 | 5(3-2-4) | |
4 | 5(4-3-2-1) | 3(4-3-2) | 2 | 0 |
(11)通过将所有的顶点分别作为“中间顶点”,最终得到的表 4 就记录了各个顶点之间的最短路径
弗洛伊德算法解决最短路径问题
1.基本思想
(1)计算图中各个顶点之间的最短路径,每一个顶点都是出发访问点,所以需要将每一个顶点看做被访问顶点,求出从每一个顶点到其他顶点的最短路径
(2)所有顶点都作为中间节点遍历一次,每次遍历将各个顶点经过中间节点到另一个节点的距离,与不经过该节点的距离相比较,若经过中间节点的距离更小,就更新距离表与前驱关系
(3)时间复杂度O(n3),所有顶点作为出发点、中间节点、终点,每个顶点都要遍历3次
2.步骤
(1)设置顶点 a 到顶点 b 的最短路径已知为 Lab,顶点 b 到 c 的最短路径已知为 Lbc,顶点 a 到 c 的路径为 Lac,则 a 到 c 的最短路径为:min ( ( Lab + Lbc ), Lac ),b 的取值为图中所有顶点,则可获得 a 到 b 的最短路径
(2)至于 a 到 b 的最短路径 Lab 或者 b 到 c 的最短路径 Lbc,是以同样的方式获得
(3)三个点为同一顶点时:中间顶点为自身;三个点是不同顶点时:中间顶点是终点的前驱节点;两个顶点直接连通时:中间节点为出发点
代码实现
import java.util.Arrays;
public class Floyd {//弗洛伊德算法解决最短路径问题
public static final int BLOCK = 65535;//表示顶点之间不直接连通
public static void main(String[] args) {
char[] vertex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
//顶点到自身距离为0
int[][] matrix = {
{0, 5, 7, BLOCK, BLOCK, BLOCK, 2},
{5, 0, BLOCK, 9, BLOCK, BLOCK, 3},
{7, BLOCK, 0, BLOCK, 8, BLOCK, BLOCK},
{BLOCK, 9, BLOCK, 0, BLOCK, 4, BLOCK},
{BLOCK, BLOCK, 8, BLOCK, 0, 5, 4},
{BLOCK, BLOCK, BLOCK, 4, 5, 0, 6},
{2, 3, BLOCK, BLOCK, 4, 6, 0}
};
Graph graph = new Graph(matrix, vertex);
graph.floyd();
graph.result();
}
}
//带权无向图
class Graph {
public char[] vertex;//存放顶点
public int[][] matrix;//保存各个顶点到其它顶点的距离,初始为直接连接的距离,算法计算后为最短距离
public int[][] relay;//保存中间结点
//构造器
public Graph(int[][] matrix, char[] vertex) {
this.vertex = vertex;
this.matrix = matrix;
this.relay = new int[vertex.length][vertex.length];
//三个点为同一顶点时:中间顶点为自身;三个点是不同顶点时:中间顶点是终点的前驱节点;两个顶点直接连通时:中间节点为出发点
for (int i = 0; i < vertex.length; i++) {
Arrays.fill(relay[i], i);//初始中间顶点为自身
}
}
//显示算法结果
public void result() {
for (int k = 0; k < vertex.length; k++) {
for (int i = 0; i < vertex.length; i++) {
System.out.println(vertex[k] + " 到 " + vertex[i] +
" 最短路径 " + matrix[k][i] +
" 中间结点 " + vertex[relay[k][i]]);
}
System.out.println();
}
}
//弗洛伊德算法
public void floyd() {
int temp;//保存i到j的距离
for (int i = 0; i < matrix.length; i++) {//出发点i
for (int j = 0; j < matrix.length; j++) {//中间顶点j
for (int k = 0; k < matrix.length; k++) {//终点k
temp = matrix[i][j] + matrix[j][k];//求从i出发,经过k,到达j的距离
if (temp < matrix[i][k]) {
matrix[i][k] = temp;//更新距离
relay[i][k] = relay[j][k];//更新中间顶点
}
}
}
}
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战