【Java数据结构与算法】第二十章 Dijkstra算法和Floyd算法

第二十章 Dijkstra算法和Floyd算法

一、Dijkstra算法

1.介绍

Dijkstra 算法是典型的单源最短路径算法,用于计算某个顶点到其他所有顶点的最短路径。Dijkstra 算法要求图中不存在负权边,即保证图中每条边的权重值为正。算法的基本思想是:从源点出发,每次选择离源点最近的一个顶点前进,然后以该顶点为中心进行扩展,最终得到源点到其余所有点的最短路径

在这里插入图片描述

在这里插入图片描述

第1步,创建距离表和前置顶点表。

距离表的 Key 是顶点名称,Value 是从起点 A 到对应顶点的已知最短距离,默认为无穷大;前置顶点表的 Key 是顶点名称,Value 是从起点A到对应顶点的已知最短路径的前置定点

在这里插入图片描述

第 2 步,遍历起点A,找到起点 A 的邻接顶点 B 和 C。从 A 到 B 的距离是 5,从 A 到 C 的距离是 2。把这一信息刷新到距离表当中

同时,顶点 B、C 的前置顶点都是 A,顶点 A 在邻接表中下标是 0,所以把前置顶点表的 B、C 值更新为 0
在这里插入图片描述

第 3 步,从距离表中找到从 A 出发距离最短的点,也就是顶点 C

第 4 步,遍历顶点 C,找到顶点 C 的邻接顶点 D 和 F(A 已经遍历过,不需要考虑)。从 C 到 D 的距离是 6,所以 A到 D 的距离是 2+6=8。从 C 到 F 的距离是 8,所以从 A 到 F 的距离是 2+8=10。把这一信息刷新到表中

同时,顶点 D、F 的前置顶点都是 C,顶点 C 在邻接表中下标是 2,所以把前置顶点表的 D、F 值更新为 2
在这里插入图片描述

接下来重复第 3 步、第 4 步所做的操作

第 5 步,也就是第 3 步的重复,从距离表中找到从A出发距离最短的点(C 已经遍历过,不需要考虑),也就是顶点 B

第 6 步,也就是第 4 步的重复,遍历顶点 B,找到顶点B的邻接顶点 D 和 E(A 已经遍历过,不需要考虑)。从 B 到 D 的距离是1,所以 A 到 D 的距离是 5+1=6,小于距离表中的 8;从 B 到 E 的距离是 6,所以从 A 到 E 的距离是 5+6=11。把这一信息刷新到表中

同时,顶点 D、E 的前置顶点都是 B,顶点 B 在邻接表中下标是 1,所以把前置顶点表的 D、E 值更新为 1
在这里插入图片描述

第 7 步,从距离表中找到从A出发距离最短的点(B 和 C 不用考虑),也就是顶点 D

第 8 步,遍历顶点 D,找到顶点 D 的邻接顶点 E 和 F。从 D 到 E 的距离是 1,所以 A 到 E 的距离是 6+1=7,小于距离表中的 11。从 D 到 F 的距离是 2,所以从 A 到 F 的距离是 6+2=8,小于距离表中的 10。把这一信息刷新到表中

同时,顶点 E、F 的前置顶点都是 D,顶点 D 在邻接表中下标是 3,所以把前置顶点表的 E、F 值更新为 3
在这里插入图片描述

第 9 步,从距离表中找到从A出发距离最短的点,也就是顶点 E

第 10 步,遍历顶点 E,找到顶点 E 的邻接顶点 G。从 E 到 G 的距离是 7,所以 A 到 G 的距离是 7+7=14。把这一信息刷新到表中

同时,顶点 G 的前置顶点是 E,顶点 E 在邻接表中下标是 4,所以把前置顶点表的 G 值更新为 4
在这里插入图片描述

第 11 步,从距离表中找到从A出发距离最短的点,也就是顶点 F。

第 12 步,遍历顶点 F,找到顶点 F 的邻接顶点 G。从 F 到 G 的距离是 3,所以 A 到 G 的距离是 8+3=11,小于距离表中的 14。把这一信息刷新到表中
在这里插入图片描述

就这样,除终点以外的全部顶点都已经遍历完毕,距离表中存储的是从起点 A 到所有顶点的最短距离,而前置定点存储的是从起点 A 到所有顶点最短路径的前置顶点

2.代码实现

package com.sisyphus.dijkstra;

import sun.awt.image.ImageWatched;

import java.util.LinkedList;
import java.util.List;

/**
 * @Description: Dijkstra 算法$
 * @Param: $
 * @return: $
 * @Author: Sisyphus
 * @Date: 7/29$
 */
public class Dijkstra {
    public static void main(String[] args) {
        Graph graph = new Graph(7);
        initGraph(graph);
        int[] prevs = dijkstra(graph, 0);
        printPrevs(graph.vertexes, prevs, graph.vertexes.length - 1);
    }

    //初始化图
    private static void initGraph(Graph graph){
        graph.vertexes[0] = new Vertex("A");
        graph.vertexes[1] = new Vertex("B");
        graph.vertexes[2] = new Vertex("C");
        graph.vertexes[3] = new Vertex("D");
        graph.vertexes[4] = new Vertex("E");
        graph.vertexes[5] = new Vertex("F");
        graph.vertexes[6] = new Vertex("G");

        graph.adj[0].add(new Edge(1, 5));
        graph.adj[0].add(new Edge(2, 2));
        graph.adj[1].add(new Edge(0, 5));
        graph.adj[1].add(new Edge(3, 1));
        graph.adj[1].add(new Edge(4, 6));
        graph.adj[2].add(new Edge(0, 2));
        graph.adj[2].add(new Edge(3, 6));
        graph.adj[2].add(new Edge(5, 8));
        graph.adj[3].add(new Edge(1, 1));
        graph.adj[3].add(new Edge(2, 6));
        graph.adj[3].add(new Edge(4, 1));
        graph.adj[3].add(new Edge(5, 2));
        graph.adj[4].add(new Edge(1, 6));
        graph.adj[4].add(new Edge(3, 1));
        graph.adj[4].add(new Edge(6, 7));
        graph.adj[5].add(new Edge(2, 8));
        graph.adj[5].add(new Edge(3, 2));
        graph.adj[5].add(new Edge(6, 3));
        graph.adj[6].add(new Edge(4, 7));
        graph.adj[6].add(new Edge(5, 3));
    }

    //图的顶点
    private static class Vertex{
        String data;
        Vertex(String data){
            this.data = data;
        }
    }

    //图的边
    private static class Edge{
        int index;
        int weight;
        Edge(int index, int weight){
            this.index = index;
            this.weight = weight;
        }
    }

    //图
    private static class Graph{
        private Vertex[] vertexes;
        private LinkedList<Edge> adj[];

        Graph(int size){
            //初始化顶点和邻接矩阵
            vertexes = new Vertex[size];
            adj = new LinkedList[size];
            for (int i = 0; i < adj.length; i++) {
                adj[i] = new LinkedList<Edge>();
            }
        }
    }

    //打印最短路径
    private static void printPrevs(Vertex[] vertexes, int[] prev, int i){
        if(i>0){
            printPrevs(vertexes, prev, prev[i]);
        }
        System.out.print(vertexes[i].data + " ");
    }

    //主算法
    private static int[] dijkstra(Graph graph, int startIndex){
        //图的顶点数量
        int size = graph.vertexes.length;
        //创建距离表,存储从起点到每一个顶点的已知最短路径的前置节点
        int[] distances = new int[size];
        //创建前置定点表,存储从起点到每一个顶点的已知最短路径的前置节点
        int[] prevs = new int[size];
        //记录顶点遍历状态
        boolean[] access  = new boolean[size];

        //初始化最短路径表,到达每个顶点的路径的距离默认为无穷大
        for (int i = 0; i < size; i++){
            distances[i] = Integer.MAX_VALUE;
        }
        //遍历起点,刷新距离表
        access[0] = true;
        List<Edge> edgesFromStart = graph.adj[startIndex];
        for (Edge edge : edgesFromStart){
            distances[edge.index] = edge.weight;
            prevs[edge.index] = 0;
        }
        //主循环,重复遍历最短距离顶点和刷新距离表的操作
        for (int i = 1; i < size; i++) {
            //寻找最短距离顶点
            int minDistanceFromStart = Integer.MAX_VALUE;
            int minDistanceIndex = -1;
            for (int j = 1; j < size; j++){
                if (!access[j] && distances[j] < minDistanceFromStart){
                    minDistanceFromStart = distances[j];
                    minDistanceIndex = j;
                }
            }
            if (minDistanceIndex == -1){
                break;
            }
            //遍历顶点,刷新距离表
            access[minDistanceIndex] = true;
            for (Edge edge : graph.adj[minDistanceIndex]){
                if (access[edge.index]){
                    continue;
                }
                int weight = edge.weight;
                int preDistance = distances[edge.index];
                if (weight != Integer.MAX_VALUE && (minDistanceFromStart+ weight < preDistance)){
                    distances[edge.index] = minDistanceFromStart + weight;
                    prevs[edge.index] = minDistanceIndex;
                }
            }
        }
        return prevs;
    }
}

时间复杂度为 O(n^2)

二、Floyd算法

1.介绍

Floyd 算法是一个经典的动态规划算法。其主要思想为:从任意顶点 u 到任意顶点v的最短路径不外乎 2 种可能,一是直接从 u 到v,二是从 u 经过若干个顶点 k 到 v。所以,我们假设 dist(u,v) 为顶点 u 到顶点 v 的最短路径的距离,对于每一个顶点 k,我们检查 dist(u,k) + dist(k,v) < dist(u,v) 是否成立,如果成立,证明从 u 到 k 再到 v 的路径比 u 直接到 v 的路径短,我们便设置 dist(u,v) = dist(u,k) + dist(k,v),这样一来,当我们遍历完所有顶点 k,dist(u,v) 中记录的便是 u 到 v 的最短路径的距离

在这里插入图片描述

第 1 步,构建带权图的邻接矩阵
在这里插入图片描述

在邻接矩阵当中,每一个数字代表着从某个顶点到另一个顶点的直接距离,这个距离是没有涉及到任何中继顶点的

第 2 步,只允许以顶点 A 作为中继顶点,B 和 C 之间的距离原本是无穷大,此时以 A 为中继,距离缩短为 AB距离 + AC距离 =
5 + 2 = 7

更新对应矩阵元素(橙色区域代表顶点A到其他顶点的临时距离)
在这里插入图片描述

第 3 步,以顶点 A、B 作为中继顶点

A 和 D 之间的距离原本是无穷大,此时以 B 为中继,距离缩短为 AB 距离 + BD距离 = 5 + 1 = 6

A 和 E 之间的距离原本是无穷大,此时以 B 为中继,距离缩短为 AB距离 + BE距离 = 5 + 6 = 11

更新对应矩阵元素(橙色区域代表顶点B到其他顶点的临时距离)
在这里插入图片描述

第 4 步,以顶点 A、B、C 作为中继顶点

A 和 F 之间的距离原本是无穷大,此时以 C 为中继,距离缩短为 AC距离 + CF距离 = 2 + 8 = 10

更新对应矩阵元素(橙色区域代表顶点C到其他顶点的临时距离)
在这里插入图片描述

以此类推,不断引入新的中继顶点,不断刷新矩阵中的临时距离

最终,当所有顶点都可以作为中继顶点时,我们的距离矩阵更新如下
在这里插入图片描述

此时,矩阵中每一个元素,都对应着某顶点到另一个顶点的最短距离

2.代码实现

package com.sisyphus.floyd;

/**
 * @Description: Floyd 算法$
 * @Param: $
 * @return: $
 * @Author: Sisyphus
 * @Date: 7/29$
 */
public class Floyd {
    final static int INF = Integer.MAX_VALUE;

    public static void main(String[] args) {
        int[][] matrix = {
                {0, 5, 2, INF, INF, INF, INF},
                {5, 0, INF, 1, 6, INF, INF},
                {2, INF, 0, 6, INF, 8, INF},
                {INF, 1, 6, 0, 1, 2, INF},
                {INF, 6, INF, 1, 0, INF, 7},
                {INF, INF, 8, 2, INF, 0, 3},
                {INF, INF, INF, INF, 7, 3, 0}
        };
        floyd(matrix);
    }

    public static void floyd(int[][] matrix){
        //循环更新矩阵的值
        for (int i=0; i<matrix.length; i++){
            for (int j=0; j<matrix.length; j++){
                for (int k=0; k<matrix.length; k++){
                    if (matrix[j][i] == INF || matrix[i][k] == INF){
                        continue;
                    }
                    matrix[j][k] = Math.min(matrix[j][k],matrix[j][i] + matrix[i][k]);
                }
            }
        }

        //打印结果
        System.out.println("最短路径矩阵:");
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix.length; j++){
                System.out.printf("%2d ",matrix[i][j]);
            }
            System.out.println();
        }
    }
}

时间复杂度为 O(n^3)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

313YPHU3

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值