SPFA算法

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容