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 {
    /**
     * 適用于單源最短路徑問題烧颖,尤其適用于權(quán)值為負(fù)的情況却盘。
     * 它適用于有向圖嘉栓。
     * 但是如果有向圖中存在回路,并且回路的權(quán)值之和為負(fù)治力,
     * 那么本圖是無法用此算法求最短路徑的,因為越求越小勃黍,
     * 根本沒有最小路徑宵统。
     * 本算法的實質(zhì)是一個廣度優(yōu)先搜索算法。
     * 又叫動態(tài)逼近法覆获。
     * 為什么spfa所有隊列中如果有一個結(jié)點马澈,那么相同的結(jié)
     * 點就不入隊呢?
     * 因為入隊是因為以該頂點為中間點能夠找到更短路徑弄息,
     * 入隊是要傳達(dá)一個信息痊班,該頂點具備可以使路徑變得更
     * 短的能力,它是一個標(biāo)識而已摹量,而有一個在隊列中就能
     * 夠標(biāo)識該頂點能夠使路徑變得更短了涤伐,就不需要第二個
     * 了馒胆。
     * 其他的也可能是為了不重復(fù)入隊,減少一些不必要的操作吧凝果,或
     * 者限制隊列的長度吧祝迂,這樣確實可以確定地設(shè)置隊列的
     * 長度。
     * 出去的點作為中間點豆村,如果該頂點的出度加上它已有的
     * 最小路徑比從源點到其箭頭頂點的距離更短液兽,則更新從
     * 源點到箭頭頂點最短路徑的值。
     * @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;
        }
        //用來存儲前驅(qū)的中間結(jié)點數(shù)組四啰,也是結(jié)果集數(shù)組。
        int[] preMiddleArray = new int[arrayLength];
        //也把它初始化為-1
        for (int counter = 0;counter < arrayLength;counter++)
            preMiddleArray[counter] = -1;
        //為了不讓已經(jīng)在隊列中的頂點標(biāo)號重復(fù)入隊粗恢,所以
        // 應(yīng)該弄一個數(shù)組來標(biāo)記有哪個頂點入隊了柑晒。
        boolean[] vertexFlag = new boolean[arrayLength];
        for (int counter = 0;counter < arrayLength;counter++)
            vertexFlag[counter] = false;
        //由于隊列中并不存在重復(fù)元素,
        // 所以隊列最長為arrayLength眷射。
        // 并且采用循環(huán)隊列匙赞。
        int[] minPathQueue = new int[arrayLength];
        int front = 0;
        int rear = front;
        minPathQueue[rear] = vertexId;
        vertexFlag[vertexId] = true;
        System.out.println("標(biāo)記數(shù)組的狀態(tài)是");
        for (boolean element:vertexFlag)
            System.out.print((element?"O":"X") + " ");
        System.out.println();
        while ((rear + 1) % arrayLength != front) {
            System.out.println("最短路徑數(shù)組狀態(tài)");
            for (int element:minPathArray)
                System.out.print(element + " ");
            System.out.println("\n前驅(qū)結(jié)點狀態(tài)");
            for (int element:preMiddleArray)
                System.out.print(element + " ");
            int currentMiddleIndex = minPathQueue[front++];
            vertexFlag[currentMiddleIndex] = false;
            System.out.println("\n頂點" + currentMiddleIndex + "出隊");
            System.out.println("并把頂點" + currentMiddleIndex + "標(biāo)記為X");
            System.out.println("標(biāo)記數(shù)組的狀態(tài)是");
            for (boolean element:vertexFlag)
                System.out.print((element?"O":"X") + " ");
            System.out.println();
            front = front % arrayLength;
            for (int counter = 0;counter < arrayLength;counter++) {
                //這個比較的術(shù)語叫做松弛,所以這一操作被稱為松弛操作妖碉。
                // 很形象涌庭,最初兩個頂點之間有根橡皮筋,后來每次找到一
                // 個能夠使路徑更短的中間點欧宜,就讓橡皮筋更松弛一些坐榆,于
                // 是松弛操作的直接意思就是找到了個中間點讓原來兩頂點
                // 之間的路徑更短了。
                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 + "入隊并被標(biāo)記為O");
                    } else System.out.println("頂點" + counter + "無需重復(fù)入隊");
                    System.out.println("標(biāo)記數(shù)組的狀態(tài)是");
                    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("最短路徑數(shù)組狀態(tài)");
                    for (int element:minPathArray)
                        System.out.print(element + " ");
                    System.out.println("\n前驅(qū)結(jié)點狀態(tài)");
                    for (int element:preMiddleArray)
                        System.out.print(element + " ");
                    System.out.println();
                }
            }
            System.out.println("頂點" + currentMiddleIndex + "處理完畢");
            System.out.println("最短路徑數(shù)組狀態(tài)");
            for (int element:minPathArray)
                System.out.print(element + " ");
            System.out.println("\n前驅(qū)結(jié)點狀態(tài)");
            for (int element:preMiddleArray)
                System.out.print(element + " ");
            System.out.println("\n");
        }
        System.out.println("此時隊列為空");
        //下面打印從源頂點到各頂點的最短路徑
        System.out.println("結(jié)果集為:");
        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("不可達(dá)");
                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();
        }
    }
}

輸出

標(biāo)記數(shù)組的狀態(tài)是
O X X X X X 
最短路徑數(shù)組狀態(tài)
0 285 285 285 285 285 
前驅(qū)結(jié)點狀態(tài)
-1 -1 -1 -1 -1 -1 
頂點0出隊
并把頂點0標(biāo)記為X
標(biāo)記數(shù)組的狀態(tài)是
X X X X X X 
頂點2入隊并被標(biāo)記為O
標(biāo)記數(shù)組的狀態(tài)是
X X O X X X 
最短路徑數(shù)組狀態(tài)
0 285 10 285 285 285 
前驅(qū)結(jié)點狀態(tài)
-1 -1 0 -1 -1 -1 
頂點4入隊并被標(biāo)記為O
標(biāo)記數(shù)組的狀態(tài)是
X X O X O X 
最短路徑數(shù)組狀態(tài)
0 285 10 285 30 285 
前驅(qū)結(jié)點狀態(tài)
-1 -1 0 -1 0 -1 
頂點5入隊并被標(biāo)記為O
標(biāo)記數(shù)組的狀態(tài)是
X X O X O O 
最短路徑數(shù)組狀態(tài)
0 285 10 285 30 100 
前驅(qū)結(jié)點狀態(tài)
-1 -1 0 -1 0 0 
頂點0處理完畢
最短路徑數(shù)組狀態(tài)
0 285 10 285 30 100 
前驅(qū)結(jié)點狀態(tài)
-1 -1 0 -1 0 0 

最短路徑數(shù)組狀態(tài)
0 285 10 285 30 100 
前驅(qū)結(jié)點狀態(tài)
-1 -1 0 -1 0 0 
頂點2出隊
并把頂點2標(biāo)記為X
標(biāo)記數(shù)組的狀態(tài)是
X X X X O O 
頂點3入隊并被標(biāo)記為O
標(biāo)記數(shù)組的狀態(tài)是
X X X O O O 
最短路徑數(shù)組狀態(tài)
0 285 10 60 30 100 
前驅(qū)結(jié)點狀態(tài)
-1 -1 0 2 0 0 
頂點2處理完畢
最短路徑數(shù)組狀態(tài)
0 285 10 60 30 100 
前驅(qū)結(jié)點狀態(tài)
-1 -1 0 2 0 0 

最短路徑數(shù)組狀態(tài)
0 285 10 60 30 100 
前驅(qū)結(jié)點狀態(tài)
-1 -1 0 2 0 0 
頂點4出隊
并把頂點4標(biāo)記為X
標(biāo)記數(shù)組的狀態(tài)是
X X X O X O 
頂點3無需重復(fù)入隊
標(biāo)記數(shù)組的狀態(tài)是
X X X O X O 
最短路徑數(shù)組狀態(tài)
0 285 10 50 30 100 
前驅(qū)結(jié)點狀態(tài)
-1 -1 0 4 0 0 
頂點5無需重復(fù)入隊
標(biāo)記數(shù)組的狀態(tài)是
X X X O X O 
最短路徑數(shù)組狀態(tài)
0 285 10 50 30 90 
前驅(qū)結(jié)點狀態(tài)
-1 -1 0 4 0 4 
頂點4處理完畢
最短路徑數(shù)組狀態(tài)
0 285 10 50 30 90 
前驅(qū)結(jié)點狀態(tài)
-1 -1 0 4 0 4 

最短路徑數(shù)組狀態(tài)
0 285 10 50 30 90 
前驅(qū)結(jié)點狀態(tài)
-1 -1 0 4 0 4 
頂點5出隊
并把頂點5標(biāo)記為X
標(biāo)記數(shù)組的狀態(tài)是
X X X O X X 
頂點5處理完畢
最短路徑數(shù)組狀態(tài)
0 285 10 50 30 90 
前驅(qū)結(jié)點狀態(tài)
-1 -1 0 4 0 4 

最短路徑數(shù)組狀態(tài)
0 285 10 50 30 90 
前驅(qū)結(jié)點狀態(tài)
-1 -1 0 4 0 4 
頂點3出隊
并把頂點3標(biāo)記為X
標(biāo)記數(shù)組的狀態(tài)是
X X X X X X 
頂點5入隊并被標(biāo)記為O
標(biāo)記數(shù)組的狀態(tài)是
X X X X X O 
最短路徑數(shù)組狀態(tài)
0 285 10 50 30 60 
前驅(qū)結(jié)點狀態(tài)
-1 -1 0 4 0 3 
頂點3處理完畢
最短路徑數(shù)組狀態(tài)
0 285 10 50 30 60 
前驅(qū)結(jié)點狀態(tài)
-1 -1 0 4 0 3 

最短路徑數(shù)組狀態(tài)
0 285 10 50 30 60 
前驅(qū)結(jié)點狀態(tài)
-1 -1 0 4 0 3 
頂點5出隊
并把頂點5標(biāo)記為X
標(biāo)記數(shù)組的狀態(tài)是
X X X X X X 
頂點5處理完畢
最短路徑數(shù)組狀態(tài)
0 285 10 50 30 60 
前驅(qū)結(jié)點狀態(tài)
-1 -1 0 4 0 3 

此時隊列為空
結(jié)果集為:
(0,1):不可達(dá)
(0,2):0-->2
(0,3):0-->4-->3
(0,4):0-->4
(0,5):0-->4-->3-->5
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末冗茸,一起剝皮案震驚了整個濱河市席镀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌夏漱,老刑警劉巖豪诲,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異挂绰,居然都是意外死亡屎篱,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門扮授,熙熙樓的掌柜王于貴愁眉苦臉地迎上來芳室,“玉大人,你說我怎么就攤上這事刹勃】昂睿” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵荔仁,是天一觀的道長伍宦。 經(jīng)常有香客問我芽死,道長,這世上最難降的妖魔是什么次洼? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任关贵,我火速辦了婚禮,結(jié)果婚禮上卖毁,老公的妹妹穿的比我還像新娘揖曾。我一直安慰自己,他們只是感情好亥啦,可當(dāng)我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布炭剪。 她就那樣靜靜地躺著,像睡著了一般翔脱。 火紅的嫁衣襯著肌膚如雪奴拦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天届吁,我揣著相機與錄音错妖,去河邊找鬼。 笑死疚沐,一個胖子當(dāng)著我的面吹牛暂氯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播亮蛔,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼株旷,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了尔邓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤锉矢,失蹤者是張志新(化名)和其女友劉穎梯嗽,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沽损,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡灯节,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了绵估。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炎疆。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖国裳,靈堂內(nèi)的尸體忽然破棺而出形入,到底是詐尸還是另有隱情,我是刑警寧澤缝左,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布亿遂,位于F島的核電站浓若,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蛇数。R本人自食惡果不足惜挪钓,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望耳舅。 院中可真熱鬧碌上,春花似錦、人聲如沸浦徊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辑畦。三九已至吗蚌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間纯出,已是汗流浹背蚯妇。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留暂筝,地道東北人箩言。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像焕襟,于是被迫代替她去往敵國和親陨收。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內(nèi)容