spfa算法 歸納

綜合看了幾位大神的歸納然後自己歸納了一下:

spfa算法處理過程:


下面摘錄大神的歸納,膜拜一下:

SPFA(Shortest Path Faster Algorithm)是Bellman-Ford算法的一種隊列實現,減少了不必要的冗餘計算。

算法大致流程是用一個隊列來進行維護。 初始時將源加入隊列。 每次從隊列中取出一個元素,並對所有與他相鄰的點進行鬆弛,若某個相鄰的點鬆弛成功,則將其入隊。 直到隊列爲空時算法結束。

這個算法,簡單的說就是隊列優化的bellman-ford,利用了每個點不會更新次數太多的特點發明的此算法

SPFA——Shortest Path Faster Algorithm,它可以在O(kE)的時間複雜度內求出源點到其他所有點的最短路徑,可以處理負邊。SPFA的實現甚至比Dijkstra或者Bellman_Ford還要簡單:

設Dist代表S到I點的當前最短距離,Fa代表S到I的當前最短路徑中I點之前的一個點的編號。開始時Dist全部爲+∞,只有Dist[S]=0,Fa全部爲0。

維護一個隊列,裏面存放所有需要進行迭代的點。初始時隊列中只有一個點S。用一個布爾數組記錄每個點是否處在隊列中。

每次迭代,取出隊頭的點v,依次枚舉從v出發的邊v->u,設邊的長度爲len,判斷Dist[v]+len是否小於Dist[u],若小於則改進Dist[u],將Fa[u]記爲v,並且由於S到u的最短距離變小了,有可能u可以改進其它的點,所以若u不在隊列中,就將它放入隊尾。這樣一直迭代下去直到隊列變空,也就是S到所有的最短距離都確定下來,結束算法。若一個點入隊次數超過n,則有負權環。

SPFA 在形式上和寬度優先搜索非常類似,不同的是寬度優先搜索中一個點出了隊列就不可能重新進入隊列,但是SPFA中一個點可能在出隊列之後再次被放入隊列,也就是一個點改進過其它的點之後,過了一段時間可能本身被改進,於是再次用來改進其它的點,這樣反覆迭代下去。設一個點用來作爲迭代點對其它點進行改進的平均次數爲k,有辦法證明對於通常的情況,k在2左右。

SPFA算法(Shortest Path Faster Algorithm),也是求解單源最短路徑問題的一種算法,用來解決:給定一個加權有向圖G和源點s,對於圖G中的任意一點v,求從s到v的最短路徑。 SPFA算法是Bellman-Ford算法的一種隊列實現,減少了不必要的冗餘計算,他的基本算法和Bellman-Ford一樣,並且用如下的方法改進: 1、第二步,不是枚舉所有節點,而是通過隊列來進行優化 設立一個先進先出的隊列用來保存待優化的結點,優化時每次取出隊首結點u,並且用u點當前的最短路徑估計值對離開u點所指向的結點v進行鬆弛操作,如果v點的最短路徑估計值有所調整,且v點不在當前的隊列中,就將v點放入隊尾。這樣不斷從隊列中取出結點來進行鬆弛操作,直至隊列空爲止。 2、同時除了通過判斷隊列是否爲空來結束循環,還可以通過下面的方法: 判斷有無負環:如果某個點進入隊列的次數超過V次則存在負環(SPFA無法處理帶負環的圖)。

SPFA算法有兩個優化算法 SLF 和 LLL: SLF:Small Label First 策略,設要加入的節點是j,隊首元素爲i,若dist(j)<dist(i),則將j插入隊首,否則插入隊尾。 LLL:Large Label Last 策略,設隊首元素爲i,隊列中所有dist值的平均值爲x,若dist(i)>x則將i插入到隊尾,查找下一元素,直到找到某一i使得dist(i)<=x,則將i出對進行鬆弛操作。 SLF 可使速度提高 15 ~ 20%;SLF + LLL 可提高約 50%。 在實際的應用中SPFA的算法時間效率不是很穩定,爲了避免最壞情況的出現,通常使用效率更加穩定的Dijkstra算法。

下面是我自己寫的模板

SPFA
 void Spfa()
 {
     for (int i(0); i<num_town; ++i)//初始化
     {
         dis[i] = MAX;
         visited[i] = false;    
     }
     queue<int> Q;
     dis[start] = 0;
     visited[start] = true;
     Q.push(start);
     while (!Q.empty()){
         int temp = Q.front();
         Q.pop();
         for (int i(0); i<num_town; ++i)
         {
             if (dis[temp] + road[temp][i] < dis[i])//存在負權的話,就需要創建一個COUNT數組,當某點的入隊次數超過V(頂點數)返回。
             {
                 dis[i] = dis[temp] + road[temp][i];
                 if (!visited[i])
                 {
                     Q.push(i);
                     visited[i] = true;    
                 }        
             }            
         }
         visited[temp] = false;            
     }    
 }


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章