美文网首页
SPFA算法

SPFA算法

作者: Stroman | 来源:发表于2018-02-23 17:23 被阅读35次
    package com.company;
    
    public class Main {
    
        public static void main(String[] args) {
        // write your code here
            int[][] testMatrix = {
                    {-1,-1,10,-1,30,100},
                    {-1,-1,5,-1,-1,-1},
                    {-1,-1,-1,50,-1,-1},
                    {-1,-1,-1,-1,0,10},
                    {-1,-1,-1,20,-1,60},
                    {-1,-1,-1,-1,-1,-1}
            };
            int[][] testMatrix0 = {
                    {-1,24,8,15,-1,-1,-1},
                    {-1,-1,-1,-1,6,-1,-1},
                    {-1,-1,-1,-1,7,3,-1},
                    {-1,-1,-1,-1,-1,-1,4},
                    {-1,-1,-1,-1,-1,-1,9},
                    {-1,-1,-1,-1,2,-1,3},
                    {-1,3,-1,-1,-1,-1,-1}
            };
            Spfa.spfa0(testMatrix,0);
        }
    }
    
    
    package com.company;
    
    public class Spfa {
        /**
         * 适用于单源最短路径问题,尤其适用于权值为负的情况。
         * 它适用于有向图。
         * 但是如果有向图中存在回路,并且回路的权值之和为负,
         * 那么本图是无法用此算法求最短路径的,因为越求越小,
         * 根本没有最小路径。
         * 本算法的实质是一个广度优先搜索算法。
         * 又叫动态逼近法。
         * 为什么spfa所有队列中如果有一个结点,那么相同的结
         * 点就不入队呢?
         * 因为入队是因为以该顶点为中间点能够找到更短路径,
         * 入队是要传达一个信息,该顶点具备可以使路径变得更
         * 短的能力,它是一个标识而已,而有一个在队列中就能
         * 够标识该顶点能够使路径变得更短了,就不需要第二个
         * 了。
         * 其他的也可能是为了不重复入队,减少一些不必要的操作吧,或
         * 者限制队列的长度吧,这样确实可以确定地设置队列的
         * 长度。
         * 出去的点作为中间点,如果该顶点的出度加上它已有的
         * 最小路径比从源点到其箭头顶点的距离更短,则更新从
         * 源点到箭头顶点最短路径的值。
         * @param sourceArray
         * @param vertexId
         */
        static public void spfa0(int[][] sourceArray,int vertexId) {
            int arrayLength = sourceArray.length;
            int maxValue = 0;
            //获取最大值
            for (int counter = 0;counter < arrayLength;counter++)
                for (int counter0 = 0;counter0 < arrayLength;counter0++)
                    if (sourceArray[counter][counter0] > 0)
                        maxValue += sourceArray[counter][counter0];
            int[] minPathArray = new int[arrayLength];
            //这里以maxValue代表无穷大。
            for (int counter = 0;counter < arrayLength;counter++) {
                if (vertexId == counter)minPathArray[counter] = 0;
                else minPathArray[counter] = maxValue;
            }
            //用来存储前驱的中间结点数组,也是结果集数组。
            int[] preMiddleArray = new int[arrayLength];
            //也把它初始化为-1
            for (int counter = 0;counter < arrayLength;counter++)
                preMiddleArray[counter] = -1;
            //为了不让已经在队列中的顶点标号重复入队,所以
            // 应该弄一个数组来标记有哪个顶点入队了。
            boolean[] vertexFlag = new boolean[arrayLength];
            for (int counter = 0;counter < arrayLength;counter++)
                vertexFlag[counter] = false;
            //由于队列中并不存在重复元素,
            // 所以队列最长为arrayLength。
            // 并且采用循环队列。
            int[] minPathQueue = new int[arrayLength];
            int front = 0;
            int rear = front;
            minPathQueue[rear] = vertexId;
            vertexFlag[vertexId] = true;
            System.out.println("标记数组的状态是");
            for (boolean element:vertexFlag)
                System.out.print((element?"O":"X") + " ");
            System.out.println();
            while ((rear + 1) % arrayLength != front) {
                System.out.println("最短路径数组状态");
                for (int element:minPathArray)
                    System.out.print(element + " ");
                System.out.println("\n前驱结点状态");
                for (int element:preMiddleArray)
                    System.out.print(element + " ");
                int currentMiddleIndex = minPathQueue[front++];
                vertexFlag[currentMiddleIndex] = false;
                System.out.println("\n顶点" + currentMiddleIndex + "出队");
                System.out.println("并把顶点" + currentMiddleIndex + "标记为X");
                System.out.println("标记数组的状态是");
                for (boolean element:vertexFlag)
                    System.out.print((element?"O":"X") + " ");
                System.out.println();
                front = front % arrayLength;
                for (int counter = 0;counter < arrayLength;counter++) {
                    //这个比较的术语叫做松弛,所以这一操作被称为松弛操作。
                    // 很形象,最初两个顶点之间有根橡皮筋,后来每次找到一
                    // 个能够使路径更短的中间点,就让橡皮筋更松弛一些,于
                    // 是松弛操作的直接意思就是找到了个中间点让原来两顶点
                    // 之间的路径更短了。
                    if (sourceArray[currentMiddleIndex][counter] > 0
                            && minPathArray[currentMiddleIndex]
                            + sourceArray[currentMiddleIndex][counter]
                            < minPathArray[counter]) {
                        if (!vertexFlag[counter]) {
                            minPathQueue[++rear % arrayLength] = counter;
                            vertexFlag[counter] = true;
                            System.out.println("顶点" + counter + "入队并被标记为O");
                        } else System.out.println("顶点" + counter + "无需重复入队");
                        System.out.println("标记数组的状态是");
                        for (boolean element:vertexFlag)
                            System.out.print((element?"O":"X") + " ");
                        System.out.println();
                        minPathArray[counter] = minPathArray[currentMiddleIndex]
                                + sourceArray[currentMiddleIndex][counter];
                        preMiddleArray[counter] = currentMiddleIndex;
                        System.out.println("最短路径数组状态");
                        for (int element:minPathArray)
                            System.out.print(element + " ");
                        System.out.println("\n前驱结点状态");
                        for (int element:preMiddleArray)
                            System.out.print(element + " ");
                        System.out.println();
                    }
                }
                System.out.println("顶点" + currentMiddleIndex + "处理完毕");
                System.out.println("最短路径数组状态");
                for (int element:minPathArray)
                    System.out.print(element + " ");
                System.out.println("\n前驱结点状态");
                for (int element:preMiddleArray)
                    System.out.print(element + " ");
                System.out.println("\n");
            }
            System.out.println("此时队列为空");
            //下面打印从源顶点到各顶点的最短路径
            System.out.println("结果集为:");
            int[] minPathStack = new int[arrayLength];
            for (int counter = 0;counter < arrayLength;counter++) {
                if (counter == vertexId)continue;
                System.out.print("(" + vertexId + "," + counter + "):");
                if (minPathArray[counter] == maxValue) {
                    System.out.println("不可达");
                    continue;
                }
                int pathStackTopPointer = -1;
                int preIndex = counter;
                while (preMiddleArray[preIndex] != vertexId) {
                    minPathStack[++pathStackTopPointer] = preIndex;
                    preIndex = preMiddleArray[preIndex];
                }
                minPathStack[++pathStackTopPointer] = preIndex;
                minPathStack[++pathStackTopPointer] = vertexId;
                while (pathStackTopPointer > -1) {
                    if (counter == minPathStack[pathStackTopPointer])
                        System.out.print(minPathStack[pathStackTopPointer--]);
                    else System.out.print(minPathStack[pathStackTopPointer--] + "-->");
                }
                System.out.println();
            }
        }
    }
    
    

    输出

    标记数组的状态是
    O X X X X X 
    最短路径数组状态
    0 285 285 285 285 285 
    前驱结点状态
    -1 -1 -1 -1 -1 -1 
    顶点0出队
    并把顶点0标记为X
    标记数组的状态是
    X X X X X X 
    顶点2入队并被标记为O
    标记数组的状态是
    X X O X X X 
    最短路径数组状态
    0 285 10 285 285 285 
    前驱结点状态
    -1 -1 0 -1 -1 -1 
    顶点4入队并被标记为O
    标记数组的状态是
    X X O X O X 
    最短路径数组状态
    0 285 10 285 30 285 
    前驱结点状态
    -1 -1 0 -1 0 -1 
    顶点5入队并被标记为O
    标记数组的状态是
    X X O X O O 
    最短路径数组状态
    0 285 10 285 30 100 
    前驱结点状态
    -1 -1 0 -1 0 0 
    顶点0处理完毕
    最短路径数组状态
    0 285 10 285 30 100 
    前驱结点状态
    -1 -1 0 -1 0 0 
    
    最短路径数组状态
    0 285 10 285 30 100 
    前驱结点状态
    -1 -1 0 -1 0 0 
    顶点2出队
    并把顶点2标记为X
    标记数组的状态是
    X X X X O O 
    顶点3入队并被标记为O
    标记数组的状态是
    X X X O O O 
    最短路径数组状态
    0 285 10 60 30 100 
    前驱结点状态
    -1 -1 0 2 0 0 
    顶点2处理完毕
    最短路径数组状态
    0 285 10 60 30 100 
    前驱结点状态
    -1 -1 0 2 0 0 
    
    最短路径数组状态
    0 285 10 60 30 100 
    前驱结点状态
    -1 -1 0 2 0 0 
    顶点4出队
    并把顶点4标记为X
    标记数组的状态是
    X X X O X O 
    顶点3无需重复入队
    标记数组的状态是
    X X X O X O 
    最短路径数组状态
    0 285 10 50 30 100 
    前驱结点状态
    -1 -1 0 4 0 0 
    顶点5无需重复入队
    标记数组的状态是
    X X X O X O 
    最短路径数组状态
    0 285 10 50 30 90 
    前驱结点状态
    -1 -1 0 4 0 4 
    顶点4处理完毕
    最短路径数组状态
    0 285 10 50 30 90 
    前驱结点状态
    -1 -1 0 4 0 4 
    
    最短路径数组状态
    0 285 10 50 30 90 
    前驱结点状态
    -1 -1 0 4 0 4 
    顶点5出队
    并把顶点5标记为X
    标记数组的状态是
    X X X O X X 
    顶点5处理完毕
    最短路径数组状态
    0 285 10 50 30 90 
    前驱结点状态
    -1 -1 0 4 0 4 
    
    最短路径数组状态
    0 285 10 50 30 90 
    前驱结点状态
    -1 -1 0 4 0 4 
    顶点3出队
    并把顶点3标记为X
    标记数组的状态是
    X X X X X X 
    顶点5入队并被标记为O
    标记数组的状态是
    X X X X X O 
    最短路径数组状态
    0 285 10 50 30 60 
    前驱结点状态
    -1 -1 0 4 0 3 
    顶点3处理完毕
    最短路径数组状态
    0 285 10 50 30 60 
    前驱结点状态
    -1 -1 0 4 0 3 
    
    最短路径数组状态
    0 285 10 50 30 60 
    前驱结点状态
    -1 -1 0 4 0 3 
    顶点5出队
    并把顶点5标记为X
    标记数组的状态是
    X X X X X X 
    顶点5处理完毕
    最短路径数组状态
    0 285 10 50 30 60 
    前驱结点状态
    -1 -1 0 4 0 3 
    
    此时队列为空
    结果集为:
    (0,1):不可达
    (0,2):0-->2
    (0,3):0-->4-->3
    (0,4):0-->4
    (0,5):0-->4-->3-->5
    

    相关文章

      网友评论

          本文标题:SPFA算法

          本文链接:https://www.haomeiwen.com/subject/ioixxftx.html