图论算法之最短路径(Dijkstra、Floyd、Bellman-ford和SPFA)

本文介绍了图论中最短路径的四种算法:Dijkstra、Floyd、Bellman-Ford和SPFA。Dijkstra算法适用于非负权图,求解单源最短路径;Floyd算法采用动态规划,计算多源最短路径;Bellman-Ford能处理含负权图的单源最短路径,同时能检测负权回路;SPFA是Dijkstra算法的优化,使用优先队列减少不必要的判断。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

图论算法之最短路径(DijkstraFloydBellman-fordSPFA

1、图论最短路径概述

图论算法为了求解一个顶点到另一个顶点的最短路径,即如果从图中某一顶点(称为源点)到达另一顶点(称为终点)的路径可能不止一条,如何找到一条路径,使得沿此路径各边上的权值总和(即从源点到终点的距离)达到最小,这条路径称为最短路径(shortestpath)。

最短路径有很多特殊的情况,包括有向图还是无向图,有没有负权边等。通常解决图论中最短路径问题的算法有DijkstraFloydBellman-fordSPFA四种算法,下面逐一介绍各个算法的思想、流程和Java代码实现。

2、Dijkstra算法

迪杰斯特拉算法主要特点是以起始点为中心向外层扩展,直到扩展到终点为止。迪杰斯特拉算法采用的是贪心策略,将Graph中的节点集分为最短路径计算完成的节点集S和未计算完成的节点集T,每次将从T中挑选V0->Vt最小的节点Vt加入S,并更新V0经由VtT中剩余节点的更短距离,直到T中的节点全部加入S中,它贪心就贪心在每次都选择一个距离源点最近的节点加入最短路径节点集合。迪杰斯特拉算法只支持非负权图,它计算的是单源最短路径,即单个源点到剩余节点的最短路径,时间复杂度为O(n²),如果求解整个图各个节点之间的最短路径,那么使用迪杰斯特拉算法的时间复杂度就是O(n³)

如果有权重是负值,则无法保证已经计算好的最短路径的节点构成最短路径,负数会影响结果。

Dijkstra算法适用场景:

一般算法书中都说适合于有向无环图(DAG)。但是并不代表无向图就不行,只要将源点和目标点的权重相互之间赋值成一个值即可。如果是有环图的话,对于每个节点都是做了一层遍历,也不会出现死循环和重复计算的问题,因此迪杰斯特拉算法是可以用在无向图和有环图中的,适合于求单源最短路径。如果想适用于多源最短路径,就要将每个节点都进行遍历,那么时间复杂度就是O(n³)

把顶点的集合V分成两组:

  • S:{已经求出最短路径的顶点}
  • T=V-S:{尚未确定最短路径的顶点}

保证两点:

①每一个顶点对应一个距离值

S中顶点:从V0到此顶点的最短路径长度

T中顶点:从V0到此顶点的只包括S中顶点作中间顶点的最短路径长度

S中各个顶点的距离值<=T中各个顶点的距离值。

将T中顶点按照最短路径递增的次序加入到S中,即每次从T中找出距离值最小的顶点加入到S中,直到S=V为止。

算法步骤:

  • 初始时,令S={V0}T={其余顶点},T中顶点Vi对应的距离值,有:
    • 若存在<V0,Vi>,为<V0,Vi>弧上的权值;
    • 若不存在<V0,Vi>,为无穷大.
  • T中选取一个其距离值为最小的顶点W,加入S,则S={V0, W}
  • 对T中其余顶点的距离值进行修改:若加进W作中间顶点,从V0Vi的距离值比不加W的路径要短,则修改此距离值;
  • 重复上述步骤,直到S中包含所有顶点,即S=V为止。

代码实现:

public class DijstraAlgorithm {
   
    //不能设置为Integer.MAX_VALUE,否则两个Integer.MAX_VALUE相加会溢出导致出现负权
    public static int MaxValue = 100000;
    
    public static void main(String[] args) {
   
        Scanner input = new Scanner(System.in);
        System.out.println("请输入顶点数和边数:");
        //顶点数
        int vertex = input.nextInt();
        //边数
        int edge = input.nextInt();
 
        int[][] matrix = new int[vertex][vertex];
        //初始化邻接矩阵
        for (int i = 0; i < vertex; i++) {
   
            for (int j = 0; j < vertex; j++) {
   
                matrix[i][j] = MaxValue;
            }
        }
        for (int i = 0; i < edge; i++) {
   
            System.out.println("请输入第" + (i + 1) + "条边与其权值:");
            int source = input.nextInt();
            int target = input.nextInt();
            int weight = input.nextInt();
            matrix[source][target] = weight;
        }
 
        //单源最短路径,源点
        int source = input.nextInt();
        //调用dijstra算法计算最短路径
        dijstra(matrix, source);
    }
 
    public static void dijstra(int[][] matrix, int source) {
   
        //最短路径长度
        int[] shortest = new int[matrix.length];
        //判断该点的最短路径是否求出
        int[] visited = new int[matrix.length];
        //存储输出路径
        String[] path = new String[matrix.length];
 
        //初始化输出路径
        for (int i = 0; i < matrix.length; i++) {
   
            path[i] = new String(source + "->" + i);
        }
 
        //初始化源节点
        shortest[source] = 0;
        visited[source] = 1;
 
        for (int i = 1; i < matrix.length; i++) {
   
            int min = Integer.MAX_VALUE;
            int index = -1;
 
            for (int j = 0; j < matrix.length; j++) {
   
                //已经求出最短路径的节点不需要再加入计算并判断加入节点后是否存在更短路径
                if (visited[j] == 0 && matrix[source][j] < min) {
   
                    min = matrix[source][j];
                    index = j;
                }
            }
 
            //更新最短路径
            shortest[index] = min;
            visited[index] = 1;
 
            //更新从index跳到其它节点的较短路径
            for (int m = 0; m < matrix.length;</
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值