• 算法笔记_071:SPFA算法简单介绍(Java)


    目录

    1 问题描述

    2 解决方案

    2.1 具体编码

     


    1 问题描述

    何为spfaShortest Path Faster Algorithm算法?

    spfa算法功能:给定一个加权连通图,选取一个顶点,称为起点,求取起点到其它所有顶点之间的最短距离,其显著特点是可以求含负权图的单源最短路径,且效率较高。(PS:引用自百度百科spfa是求单源最短路径的一种算法,它还有一个重要的功能是判负环(在差分约束系统中会得以体现),在Bellman-ford算法的基础上加上一个队列优化,减少了冗余的松弛操作,是一种高效的最短路算法。

    spfa算法思想:spfa就是BellmanFord的一种实现方式,其具体不同在于,对于处理松弛操作时,采用了队列(先进先出方式)操作,从而大大提高了时间复杂度。 (PS:对于BellmanFord算法可以参考本人的另一篇文章算法笔记_070:BellmanFord算法简单介绍(Java))


    2 解决方案

    2.1 具体编码

    spfa算法寻找单源最短路径的时间复杂度为O(m*E)。(其中m为所有顶点进队的平均次数,可以证明m一般小于等于2*图顶点个数E为给定图的边集合)

    首先看下代码中所使用的连通图(PS:改图为无向连通图,所以每两个顶点之间均有两条边):

     

    现在求取上图中顶点B到其它所有顶点之间的最短距离。

    具体代码如下(PS:下面代码中对于图的处理是直接遍历所有边,如果把该方法变成使用邻接表来实现,时间效率会更好一点,详见:算法笔记_075:蓝桥杯练习 最短路(Java)中方法2):

    package com.liuzhen.chapter9;
    
    import java.util.ArrayList;
    import java.util.Scanner;
    
    public class Spfa {
        
        public long[] result;         //用于得到第s个顶点到其它顶点之间的最短距离
        
        //内部类,用于存放图的具体边数据
        class edge {
            public int a;  //边的起点
            public int b;  //边的终点
            public int value;   //边的权值
            
            edge(int a, int b, int value) {
                this.a = a;
                this.b = b;
                this.value = value;
            }
        }
        /*
         * 参数n:给定图的顶点个数
         * 参数s:求取第s个顶点到其它所有顶点之间的最短距离
         * 参数edge:给定图的具体边
         * 函数功能:如果给定图不含负权回路,则可以得到最终结果,如果含有负权回路,则不能得到最终结果
         */
        public boolean getShortestPaths(int n, int s, edge[] A) {
            ArrayList<Integer> list = new ArrayList<Integer>();
            result = new long[n];
            boolean[] used = new boolean[n];
            int[] num = new int[n];
            for(int i = 0;i < n;i++) {
                result[i] = Integer.MAX_VALUE;
                used[i] = false;
            }
            result[s] = 0;     //第s个顶点到自身距离为0
            used[s] = true;    //表示第s个顶点进入数组队
            num[s] = 1;       //表示第s个顶点已被遍历一次
            list.add(s);      //第s个顶点入队
            while(list.size() != 0) {
                int a = list.get(0);   //获取数组队中第一个元素
                list.remove(0);         //删除数组队中第一个元素
                for(int i = 0;i < A.length;i++) {
                    //当list数组队的第一个元素等于边A[i]的起点时
                    if(a == A[i].a && result[A[i].b] > result[A[i].a] + A[i].value) { 
                        result[A[i].b] = result[A[i].a] + A[i].value;
                        if(!used[A[i].b]) {
                            list.add(A[i].b);
                            num[A[i].b]++;
                            if(num[A[i].b] > n)
                                return false;
                            used[A[i].b] = true;   //表示边A[i]的终点b已进入数组队
                        }
                    }
                }
                used[a] = false;        //顶点a出数组对
            }
            return true;
        }
        
        public static void main(String[] args) {
            Spfa test = new Spfa();
            Scanner in = new Scanner(System.in);
            System.out.println("请输入一个图的顶点总数n起点下标s和边总数p:");
            int n = in.nextInt();
            int s = in.nextInt();
            int p = in.nextInt();        
            edge[] A = new edge[p];
            System.out.println("请输入具体边的数据:");
            for(int i = 0;i < p;i++) {
                 int a = in.nextInt();
                 int b = in.nextInt();
                 int value = in.nextInt();
                 A[i] = test.new edge(a, b, value);
            }
            if(test.getShortestPaths(n, s, A)) {
                 for(int i = 0;i < test.result.length;i++)
                     System.out.print(test.result[i]+" ");
            } else
                 System.out.println("给定图存在负环,没有最短距离");
        }
    }

    运行结果:

    请输入一个图的顶点总数n起点下标s和边总数p:
    6 1 18
    请输入具体边的数据:
    0 1 6
    0 2 3
    1 2 2
    1 3 5
    2 3 3
    2 4 4
    3 4 2
    3 5 3
    4 5 5
    1 0 6
    2 0 3
    2 1 2
    3 1 5
    3 2 3
    4 2 4
    4 3 2
    5 3 3
    5 4 5
    5 0 2 5 6 8 

    参考资料:

    1. SPFA算法详解

  • 相关阅读:
    CSS 总结
    C#实现网页表单自动提交
    解析XML【C#】
    用正则表达式替换指定标签中的内容
    C#实现在winfrom程序中下载文件
    DataGridView添加复选框并向其中绑定值
    二进制数与十六进制数之间如何互相转换
    MySql数据库表类型MYISAM与InnoDB的区别
    php引用(&)变量引用,函数引用,对象引用和参数引用用法
    从git中删除 .idea 目录
  • 原文地址:https://www.cnblogs.com/liuzhen1995/p/6535025.html
Copyright © 2020-2023  润新知