SPFA算法【最短路径】
来源:互联网 发布:童谣的诡计知乎 编辑:程序博客网 时间:2024/05/12 11:54
SPFA算法
只要最短路径存在,SPFA算法必定能求出最小值,SPFA对Bellman-Ford算法优化的关键之处在于意识到:只有那些在前一遍松弛中改变了距离估计值的点,才可能引起他们的邻接点的距离估计值的改变。为什么队列为空就不改变了呢?就是因为要到下一点必须经过它的前一个邻接点。。SPFA可以处理负权边。很多时候,给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。简洁起见,我们约定有向加权图G不存在负权回路,即最短路径一定存在。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路。
初始化: dis数组全部赋值为Inf(无穷大,不能是map[s][i]),path数组全部赋值为s(即源点),或者赋值为-1,表示还没有知道前驱,然后dis[s]=0; 表示源点不用求最短路径,或者说最短路就是0。将源点入队;另外记住在整个算法中有顶点入队了要记得标记vis数组,有顶点出队了记得消除那个标记(可能多次入队)。
核心:读取队头顶点u,并将队头顶点u出队(记得消除标记);将与点u相连的所有点v进行松弛操作,如果能更新估计值(即令d[v]变小),那么就更新,另外,如果点v没有在队列中,那么要将点v入队(记得标记),如果已经在队列中了,那么就不用入队以此循环,直到队空为止就完成了单源最短路的求解。
判断有无负环:如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图),假设这个节点的入度是k(无向权则就是这个节点的连接的边)如果进入这个队列超过k,说明必然有某个边重复了,即成环;换一种思路:用DFS,假设存在负环a1->a2->…->an->a1。那么当从a1深搜下去时又遇到了a1,那么直接可以判断负环了所有用。当某个节点n次进入队列,则存在负环,此时时间复杂度为O(n*m),n为节点,m为边。
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算法。个人觉得LLL优化每次要求平均值,不太好,为了简单,我们可以之间用c++STL里面的优先队列来进行SLF优化。
#include <iostream>#include <deque>#include <stack>#include <vector>using namespace std; const int MAXN=100;const int INF=0x7FFFFFFF; struct edge{ int to,weight;}; vector<edge> adjmap[MAXN];//邻接表bool in_queue[MAXN];//顶点是否在队列中int in_sum[MAXN];//顶点入队次数int dist[MAXN];//源点到各点的最短路径int path[MAXN];//存储到达i的前一个顶点int nodesum;//顶点数int edgesum;//边数 bool SPFA(int source){ deque<int> dq; int i,j,x,to; for(i=1;i<=nodesum;i++) { in_sum[i]=0; in_queue[i]=false; dist[i]=INF; path[i]=-1; } dq.push_back(source); in_sum[source]++; dist[source]=0; in_queue[source]=true;//初始化完成 while(!dq.empty()) { x=dq.front(); dq.pop_front(); in_queue[x]=false; for(i=0;i<adjmap[x].size();i++) { to=adjmap[x][i].to; if((dist[x]<INF)&&(dist[to]>dist[x]+adjmap[x][i].weight)) { dist[to]=dist[x]+adjmap[x][i].weight; path[to]=x; if(!in_queue[to]) { in_queue[to]=true; in_sum[to]++; if(in_sum[to]==nodesum) return false; if(!dq.empty()) { if(dist[to]>dist[dq.front()]) dq.push_back(to); else dq.push_front(to); }else dq.push_back(to); } } } } return true;} void Print_Path(int x){ stack<int> s; int w=x; while(path[w]!=-1) { s.push(w); w=path[w]; } cout<<"顶点1到顶点"<<x<<"的最短路径长度为:"<<dist[x]<<endl; cout<<"所经过的路径为:1"; while(!s.empty()) { cout<<s.top()<<""; s.pop(); } cout<<endl;} int main(){ int i,s,e,w; edge temp; cout<<"输入顶点数和边数:"; cin>>nodesum>>edgesum; for(i=1;i<=nodesum;i++) adjmap[i].clear();//清空邻接表 for(i=1;i<=edgesum;i++) { cout<<"输入第"<<i<<"条边的起点、终点还有对应的权值:"; cin>>s>>e>>w; temp.to=e; temp.weight=w; adjmap[s].push_back(temp); } if(SPFA(1)) { for(i=2;i<=nodesum;i++) Print_Path(i); } else cout<<"图中存在负权回路"<<endl; return 0;}
- 最短路径算法---SPFA
- 最短路径----SPFA算法
- 【最短路径】SPFA算法
- SPFA算法【最短路径】
- 最短路径SPFA算法
- 最短路径算法之SPFA算法
- POJ_2394_最短路径---SPFA算法
- 最短路径之SPFA算法
- 最短路径 之 SPFA算法
- 最短路径的SPFA算法
- 【转载】最短路径之SPFA算法
- 最短路径问题 SPFA算法
- 最短路径 之 SPFA算法
- 最短路径——SPFA算法
- 最短路径之spfa算法
- 最短路径之Spfa算法
- 最短路径 之 SPFA算法
- 【算法】最短路径之SPFA
- 深入理解Java内存模型 读书总结
- 马士兵 Servlet & JSP(1) Servlet (源代码)
- sicily 1194 message blood 之失败篇
- Android NDK工程的编译和链接以及使用gdb进行调试
- 子数组的最大乘积
- SPFA算法【最短路径】
- ios中的#include,#import和@class
- 装饰模式
- 什么是动态规划?动态规划的意义是什么?
- 归并排序求逆序对
- STM32固件库的使用
- 马士兵Servlet&Jsp学习
- Camera 图像处理原理分析- 抗噪 变焦 频闪 等
- 第三章第15题