SPFA算法
SPFA算法是Bellman_Ford的一种队列改进,减少了不必要的沉余计算;相比于Dijkstra算法的优点是可以用来在负权图上求最短路,且平均情况下复杂度也较优;
算法思想:用一个队列来从源点开始维护,使得队列中的每个点都与它相连的点进行松弛操作;若松弛成功,则入队;否则开始下一个点的松弛;直到队列为空;
从上不难看出,其本质就是BFS的过程,核心部分就是松弛;
和BFS不同的是,bfs时每个点都最多只入队一次,而SPFA算法中每个点可以多次入队;也就是说,一个点在改进其它的点后,本身有可能再次被其它的点改进,然后加入队列再次准备改进其它的点,如此反复迭代......直到所有的点都无法再改进;此时,队列为空。
SPFA算法是一种求解单源最短路径的算法,也就是说,它可以求解某个点 S 到图中其它所有的点的距离;所以我们用dist[i]来表示 S 到 i 的最短距离。另外,它还可以检查出负环,判断有无负环:若某个点的入队次数超过 V (代表顶点数)次,则存在负环,所以我们用count[i] 来表示 i 点进入队列的次数;
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算法。
1 void SPFA(int s) 2 { 3 queue<int> Q; 4 _clr(used, 0); 5 _clr(dist, INF); // dist要初始化为INF 6 used[s] = true; 7 dist[s] = 0; 8 Q.push(s); 9 while(!Q.empty()) 10 { 11 int u = Q.front(); 12 Q.pop(); 13 used[u] = false; // u点出队 14 for(int v=1; v<=cnt; v++) 15 { 16 if(dist[u] + edge[u][v] < dist[v]) // 松弛成功 17 { 18 dist[v] = dist[u] + edge[u][v]; 19 if(!used[v]) 20 { 21 used[v] = true; 22 Q.push(v); 23 } 24 } 25 } 26 } 27 }