最短路径算法之SPFA算法

    xiaoxiao2021-03-25  58

    适用范围:

    给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。 我们约定有向加权图G不存在负权回路,即最短路径一定存在。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点。

    算法思想:

    我们用数组d记录每个结点的最短路径估计值,用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止

    期望的时间复杂度O(ke), 其中k为所有顶点进队的平均次数,可以证明k一般小于等于2。

    算法实现过程:

     建立一个队列,初始时队列里只有起始点,再建立一个表格记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。然后执行松弛操作,用队列里有的点作为起始点去刷新到所有点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。重复执行直到队列为空。 判断有无负环:   如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图) 首先建立起始点a到其余各点的 最短路径表格

    nodeabcdefgd[i]0∞∞∞∞∞∞

    首先源点a入队,当队列非空时:  1、队首元素(a)出队,对以a为起始点的所有边的终点依次进行松弛操作(此处有b,c,d三个点),此时路径表格状态为:

    out of queue nodenodes in queuenodes in queuenodes in queueabcd024815

    在松弛时三个点的最短路径估值变小了,而这些点队列中都没有出现,这些点需要入队,此时,队列中新入队了三个结点b,c,d 队首元素b点出队,对以b为起始点的所有边的终点依次进行松弛操作(此处只有e点),此时路径表格状态为:

    out of queue nodenodes in queuenodes in queuenodes in queuebcde2481530

    在最短路径表中,e的最短路径估值也变小了,e在队列中不存在,因此e也要入队,此时队列中的元素为c,d,e 队首元素c点出队,对以c为起始点的所有边的终点依次进行松弛操作(此处有e,f两个点),此时路径表格状态为:

    out of queue nodenodes in queuenodes in queuenodes in queuecdef8151511

    在最短路径表中,e,f的最短路径估值变小了,e在队列中存在,f不存在。因此e不用入队了,f要入队,此时队列中的元素为d,e,f 队首元素d点出队,对以d为起始点的所有边的终点依次进行松弛操作(此处只有g这个点),此时路径表格状态为:

    out of queue nodenodes in queuenodes in queuenodes in queuedefg15151119

    在最短路径表中,g的最短路径估值没有变小(松弛不成功),没有新结点入队,队列中元素为f,g 队首元素f点出队,对以f为起始点的所有边的终点依次进行松弛操作(此处有d,e,g三个点),此时路径表格状态为:

    out of queue nodenodes in queuenodes in queuenodes in queueefg151119

    在最短路径表中,e,g的最短路径估值又变小,队列中无e点,e入队,队列中存在g这个点,g不用入队,此时队列中元素为g,e 队首元素g点出队,对以g为起始点的所有边的终点依次进行松弛操作(此处只有b点),此时路径表格状态为:

    out of queue nodenodes in queuenodes in queuenodes in queuegeb141317

    在最短路径表中,b的最短路径估值又变小,队列中无b点,b入队,此时队列中元素为e,b 队首元素e点出队,对以e为起始点的所有边的终点依次进行松弛操作(此处只有g这个点),此时路径表格状态为:

    out of queue nodenodes in queuenodes in queuenodes in queueeb1317

    在最短路径表中,g的最短路径估值没变化(松弛不成功),此时队列中元素为b 队首元素b点出队,对以b为起始点的所有边的终点依次进行松弛操作(此处只有e这个点),此时路径表格状态为:

    out of queue nodenodes in queuenodes in queuenodes in queuebnull17null

    在最短路径表中,e的最短路径估值没变化(松弛不成功),此时队列为空了 最终a到g的最短路径为14

    实现代码

    package code.cdn.src.com.utils; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.Map.Entry; import java.util.Queue; import java.util.Scanner; public class SPFA { private double[][] map; // 邻接矩阵 private double[] dist; // s到终点的最短路径 // private int[] Path;// 记录前驱点 。若Path[i]=k,表示从s到i的最短路径中i的前一个点是k public static final int INF = 10000; private int n;// 顶点个数 private int s;// 起点 private int e;// 终点 private HashMap<Pairs, ArrayList<Integer>> shortPathLinkMap; //用于存放最短路径的路径链路信息 public SPFA(){ //空的构造方法,用于手动set,get } public SPFA(int n, int s, int e, double[][] map) { this.n = n; this.s = s; this.e = e; this.map = map; dist = new double[n]; shortPathLinkMap = new HashMap<Pairs, ArrayList<Integer>>(); } /** * @author Zhang_Li * 算法的逻辑主要在函数spfa()里实现 */ public void spfa() { // 首先初始化dist矩阵 全部设置成INF for (int i = 0; i < dist.length; i++) { dist[i] = INF; } Queue<Integer> queue = new LinkedList<Integer>(); // 创建一个队列 queue.offer(s); // 开源节点入队 dist[s] = 0; // 对应的开始节点的距离设置成0 shortPathLinkMap.put(new Pairs(s,s), new ArrayList<Integer>()); while (!queue.isEmpty()) { int poll = queue.poll(); // 第一次执行的时候得到开始节点 // 根据map记录的信息,得到与poll直接连接的节点 ArrayList<Integer> t_List = shortPathLinkMap.get(new Pairs(s,poll)); for (int i = 0; i < map[poll].length; i++) { if (map[poll][i] < INF || map[i][poll] < INF) { if (!queue.contains(i)) { if (dist[i] > dist[poll] + map[poll][i]) { queue.add(i); Pairs t_Pairs = new Pairs(s,i); ArrayList<Integer> t_List1 = new ArrayList<Integer>(); t_List1.addAll(t_List); t_List1.add(i); shortPathLinkMap.put(t_Pairs, t_List1); //printLog(t_Pairs, t_List1); // if(!shortPathLinkMap.containsKey(t_Pairs)){ // ArrayList<Integer> t_List1 = new ArrayList<Integer>(); // t_List1.addAll(t_List); // t_List1.add(i); // shortPathLinkMap.put(t_Pairs, t_List1); // printLog(t_Pairs, t_List1); // }else{ // // // ArrayList<Integer> being_List = shortPathLinkMap.get(t_Pairs); // being_List.add(i); // shortPathLinkMap.put(t_Pairs,being_List); // printLog(t_Pairs, being_List); // } dist[i] = dist[poll] + map[poll][i]; } } else { if (map[poll][i] + dist[poll] < dist[i]) { ArrayList<Integer> being_List; if(!shortPathLinkMap.containsKey(new Pairs(s,poll))){ being_List = new ArrayList<Integer>(); }else{ being_List = shortPathLinkMap.get(new Pairs(s,poll)); } ArrayList<Integer> copy_List = new ArrayList<Integer>(); copy_List.addAll(being_List); copy_List.add(i); Pairs new_Pairs = new Pairs(s,i); shortPathLinkMap.put(new_Pairs,copy_List); //printLog(new_Pairs, copy_List); dist[i] = map[poll][i] + dist[poll]; } } } } // for (int i : dist) { // System.out.print(i + " "); // } // System.out.println(); } } /** * 打印一下log * @param t_Pairs * @param t_List */ private static void printLog(Pairs t_Pairs, ArrayList<Integer> t_List) { System.out.print(t_Pairs + " "); for (int k : t_List) { System.out.print(k + " "); } System.out.println(); } public void SPFAByMe(int s, int e, double[][] graphMatrix) { //初始化... this.s = s; //开始节点 this.e = e; //终结节点 this.map = graphMatrix; // 邻接矩阵 this.n = graphMatrix.length; this.dist = new double[n]; shortPathLinkMap = new HashMap<Pairs, ArrayList<Integer>>(); spfa(); //包装一下 } public static void main(String[] args) { Scanner sc = new Scanner(System.in); // n:图的定点数 n:边数 s:开始节点 e:目标节点 u:一个节点 v:另一个节点 w:两个节点u、v之间的连接权值 int n, m, s, e, u, v, w; double[][] map; while (sc.hasNext()) { n = sc.nextInt(); // 图的顶点数 m = sc.nextInt(); // 边数 map = new double[n][n]; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) map[i][j] = INF; while (m-- > 0) { u = sc.nextInt(); v = sc.nextInt(); w = sc.nextInt(); // if (map[u][v] > w || map[v][u] > w) { //注意是双向的 边的权值不是无限大 // map[u][v] = w; //是无向图 // map[v][u] = w; //是无向图 // } if (map[u][v] > w) { // 注意是双向的 边的权值不是无限大 map[u][v] = w; // 是无向图 } } s = sc.nextInt(); e = sc.nextInt(); SPFA ma = new SPFA(n, s, e, map); ma.spfa(); double[] dist = ma.getDist(); if (dist[e] == INF) System.out.println(-1); else System.out.println(dist[e]); HashMap<Pairs, ArrayList<Integer>> shortPathLinkMap = ma.getShortPathLinkMap(); Iterator<Entry<Pairs, ArrayList<Integer>>> it = shortPathLinkMap.entrySet().iterator(); while (it.hasNext()){ Entry<Pairs, ArrayList<Integer>> next = it.next(); Pairs key = next.getKey(); ArrayList<Integer> value = next.getValue(); //printLog(key, value); } } } public double[][] getMap() { return map; } public void setMap(double[][] rentCapRatio) { this.map = rentCapRatio; } public int getN() { return n; } public void setN(int n) { this.n = n; } public int getS() { return s; } public void setS(int s) { this.s = s; } public int getE() { return e; } public void setE(int e) { this.e = e; } public HashMap<Pairs, ArrayList<Integer>> getShortPathLinkMap() { return shortPathLinkMap; } public void setShortPathLinkMap(HashMap<Pairs, ArrayList<Integer>> shortPathLinkMap) { this.shortPathLinkMap = shortPathLinkMap; } public void setDist(double[] dist) { this.dist = dist; } public double[] getDist() { return dist; } }

    reference:

    http://lib.csdn.net/article/datastructure/10344

    转载请注明原文地址: https://ju.6miu.com/read-35403.html

    最新回复(0)