Floyd算法 Java实现

算法导入

在上一篇博客中,咱讲述了求单源最短路径的一种经典算法 Dijkstra 算法,想了解的同学可以走前门瞅一瞅,记得回来哈。

经典Dijkstra算法 Java实现

但是因为算法的局限性,一是不能处理非负权图,二是只能处理单源到其他点的最短路径。有些小伙伴肯定不满意了呀!别急,今天咱介绍另一种的算法,Floyd算法,而且实现极其简单,for就完事了。先上图,咱梳理一下最短路中各种算法的差异。

请添加图片描述

算法核心

Floyd算法的核心思想为动态规划,动态规划是一种通过把原问题分解为相对简单的子问题的方式求解复杂问题。最简单的动态规划,斐波纳契数列,也叫兔子数列(兔子繁殖例子)1,1,2,3,5,8,13,21…

对于上面的数列,我们可以首先确定边界条件是 F(0) = 1, F(1) = 1, 当 n >= 2时,F(n) = F(n - 1) + F(n-2)。当转移方程确定后,就能够方便的求出后者了。

那么在Floyd算法中,定义一个数组 f[k][x][y] 表示 只允许经过 结点1到 k,结点 x到 y的最短路径长度,也就是说,当 k = 6 时,可能 x - 1 - y 最短,也可能 x - 2 - 5 - 3 - y 最短,在满足条件下,f[k][x][y] = min(最短路径)。假设有n个结点,编号1 - n,那么可以确定:

  • f[n][x][y]:结点 x 到 y 的最短路径。
  • f[0][x][y]:不经过任何结点,x -> y 的最短路径。若两者直接相连,x -> y = w(x, y) 最短路径为权值,或者两者不直接相连,为正无穷

确定边界条件之后,我们来确定转移方程,对于任何一个f[k][x][y], 有两种到达方式:

  1. 不经过k 个结点,那么 f[k][x][y] = f[k - 1][x][y];
  2. 经过k 个结点了,那么 f[k][x][y] = f[k - 1][x][k] + f[k - 1][k][y]
  3. 两者取最小值。

那么最终的转移方程即可确定 f[k][x][y] = min(f[k-1][x][y], f[k-1][x][k] + f[k-1][k][y] )

可以看到 如果顺序求值,第一维对结果并没有影响,那么可以转变为:f[x][y] = min(f[x][y], f[x][k] + f[k][y])

转移方程有了,边界条件有了,那么代码就水到渠成了。

代码实现

import java.util.*;

/**
 * @author LKQ
 * @date 2022/4/21 18:52
 * @description Floyd算法,适用于任何图,且不管有向无向,边权正负。但是最短路必须存在
 * 时间复杂度为O(n^3),适用于稠密图,即 m(边的数量)  约等于 n^2 (点的数量)
 */
public class Solution {
    /**
     * 邻接矩阵存储
     */
    int[][] graph;

    int INF = Integer.MAX_VALUE / 2;
    /**
     * 多源最短路算法
     * @param n n个结点 编号 0 - n-1
     * @param edges 边 e[0] - e[1] 的权值为 e[2]
     */
    public void floyd(int n, int[][] edges) {
        // 1. 初始化邻接矩阵
        graph = new int[n][n];
        for (int i = 0; i < n; i++) {
            // 点到其他点的距离为无穷大
            Arrays.fill(graph[i], INF);
            // 点到自身的距离为0
            graph[i][i] = 0;
        }
        // 注意有向图和无向图的区别,无向图 u -> v,v -> u 都要设置,有向图 只要设置 u -> v
        for(int[] e: edges) {
            graph[e[0]][e[1]] = e[2];
            graph[e[1]][e[0]] = e[2];
        }
        // floyd 基本流程为三层循环:
        // 枚举中转点 - 枚举起点 - 枚举终点 - 松弛操作
        for (int k = 0; k < n; k++) {
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    graph[i][j] = Math.min(graph[i][j], graph[i][k] + graph[k][j]);
                }
            }
        }
    }
}

核心代码只有三个for循环,是不是很简单

没骗你们吧。但是要注意一点,由于时间复杂度为 O(n^3),而且空间复杂度 为 O(n ^ 2),当点较多时,那么就有可能造成 TLE 或 MLE ,做题目的时候尤其需要小心。

参考资料

OI Wiki
图灵程序设计丛书 算法 第4版

结尾

琅琅书声如春风,拂过千年时空
少年啊壮志在胸, 赋首词让人感动
许嵩 / 孙涛 《书香年华》

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值