最短路径——SPFA算法

来源:互联网 发布:数据近义词 编辑:程序博客网 时间:2024/05/23 19:56

关于SPFA(Shortest Path Faster Algorithm)算法,网上的实现与叙述已经有很多,所以在这里也不多说,在NOCOW上面有详细述说。

这个算法是对Bellman_Ford算法的一个队列优化,减少冗余计算。在计算带有负值边的图的最短路问题时是非常好的选择,当然在差分约束系统问题中也是首选。

这个算法的一个特点是,每个顶点可以进队不仅一次,在图中存在负权回路的时候,在该回路上的顶点入队次数会超过顶点总数,这也是判断图中是否存在负权回路的条件。

下面的算法是利用双端队列对一般的SPFA进行SLF优化的实现代码(SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j)<dist(i),则将j插入队首,否则插入队尾。)

未优化的SPFA算法伪码如下

Procedure SPFA; Begin  initialize-single-source(G,s);  initialize-queue(Q);  enqueue(Q,s);  while not empty(Q) do     begin      u:=dequeue(Q);      for each v∈adj[u] do         begin          tmp:=d[v];          relax(u,v);          if (tmp<>d[v]) and (not v in Q) then            enqueue(Q,v);        end;    end;End;

下面是利用邻接表为存储结构,对SPFA进行SLF优化,并可以判断是否存在负权回路的C++实现

#include <iostream>#include <deque>#include <stack>#include <vector>using namespace std;#define MAXN 100#define INF 99999999struct edge{int to;int 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);elsedq.push_front(to);}elsedq.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);}elsecout<<"图中存在负权回路"<<endl;return 0;}

下面是两个测试,第一个生成的图是和Bellman_Ford算法中的测试数据一样的,这里就不再给出该图,可以通过连接去该文章中查看。

第二个测试是具有负权回路的图



下面是第二个测试的图,将其中顶点s编号为1,其余顶点的编号按照字母顺序编号。


原创粉丝点击