关于最短路的几个算法有Dijkstra,Bellman-Ford,Floyd
Dijkstra:
Dijkstra适用于边权为正的情况,从单个源点出发,到其他所有结点的最短路
算法的核心是用已经知道的结点 i 的距离 d[i] 去更新和这个结点相连的其他结点的距离
void Dijkstra() { memset(vis,0,sizeof(vis)); //vis数组表示结点是否被访问 memset(d,INF,sizeof(d)); //d数组表示到结点的距离 d[s]=0; //将起点的距离设为0 for(int i=1;i<=n;i++) { int tmp,minn=INF; for(int j=1;j<=n;i++) { if(!vis[j] && d[j]<minn) { minn=d[j]; tmp=j; } } //在所有已经问访问过的结点中去找一个距离最小的结点 vis[tmp]=1; for(int j=1;j<=n;j++) if(d[tmp]+w[tmp][j]<d[j]) d[j]=d[tmp]+w[tmp][j]; //用最小的结点去更新和连接的结点的距离 } }
这样将n个结点都更新之后便可以得到起点到其他结点的距离,这个写法的复杂度是O(n^2)
但有时候边没有那么多也就是稀疏图,便可以用优先队列优化到O(mlogn)
struct Edge { int from,to,dis; Edge(int u,int v,int d):from(u),to(v),dis(d) {} }; struct node { int d,u; //d是距离,u是另一个结点的编号 bool operator<(const node A) const { return d>A.d; } }; vector<Edge> edges; vector<int> G[MAXN]; int vis[MAXN]; //判断结点是否被访问 int p[MAXN]; //最短路上的一条弧 int d[MAXN]; //起点s到各点的距离 void Dijkstra() { priority_queue<node> Q; memset(vis,0,sizeof(vis)); for(int i=1;i<=n;i++) d[i]=INF; d[s]=0; node start; start.d=0,start.u=s; Q.push(start); while(!Q.empty()) { node x=Q.top;Q.pop(); int u=x.u; if(vis[u]) continue; vis[u]=1; for(itn i=0;i<G[u].size();i++) { Edge& e=edges[G[u][i]]; if(d[e.to]>d[u]+e.dis) { node next; d[e.to]=d[u]+e.dis; p[e.to]=G[u][i]; next.d=d[e.to]; next.u=e.to; Q.push(next); } } } }
其核心思想还是没有变,依旧是用已经知道的点去更新起点s到其他点的距离,用优先队列,优先级最大的是距离最小的点,然后用这个点去更新其他点
最坏的情况下仍然会循环n-1次,但是从整体上看,每条边恰好被检查过一次,所以松弛操作的执行次数恰好是m次,这样只用找未被访问的d的最小值即可
可以说即使是稠密图,用了priority_queue还是会比用邻接矩阵的Dijstra算法快,因为不满足d[e.to]>d[u]+e.dis是不能入队的,所有push操作会很少
Bellman-Ford:
Bellman-Ford也是一个求最短路的算法,这个算法用于算最短路的时候,如果最短路存在,那么一定是一个不含环的最短路,那么这个算法还有一个用处就是判环,
如果存在负环的话,那么便不会存在最短路(因为会不断松弛下去)
如果不含环的话,那么最多便通过n-1个结点(不包含起点),通过n-1次松弛操作便可以计算出最短路
算法核心就是通过循环n-1次,每次对所有的边进行松弛操作边去更新结点距离
for(int i=1;i<=n;i++) d[i]=INF; d[0]=0; for(int i=0;i<n-1;i++) //迭代n-1次 { for(int j=0;j<m;j++) //检查每一条边 { int x=u[i],y=v[i]; if(d[x]<INF) { d[y]=min(d[y],d[x]+w[i]); //松弛操作 } } }
这个代码很明显没有进行负环的判定,复杂度是O(nm)
可以用FIFO队列进行优化,可以进行循环检查
struct Edge { int from,to,dis; Edge(int u,int v,int d):from(u),to(v),dis(d) {} }; vector<Edge> edges; vector<int> G[MAXN]; int vis[MAXN]; //判断结点是否被访问 int p[MAXN]; //最短路上的一条弧 int d[MAXN]; //起点s到各点的距离 bool Bellman-Ford(int s) { queue<int> Q; memset(inq,0,sizeof(inq)); memset(cnt,0,sizeof(cnt)); for(int i=0;i<n;i++) d[i]=INF; d[s]=0; inq[s]=true; Q.push(s); while(!Q.empty()) { int u=Q.front();Q.pop(); inq[u]=false; for(int i=0;i<G[u].size();i++) { Edge& e=edges(G[u][i]); if(d[u]<INF && d[e.to]>d[u]+e.dis) //松弛 { d[e.to]=d[u]+e.dis; p[e.to]=G[u][i]; if(!inq[e.to]) { Q.push(e.to); inq[e.to]=true; if(++cnt[e.to]>n) return false; //如果一个点入队大于n次,那么一定存在负环 } } } return true; } }
用队列优化的Bellman-Ford也叫SPFA(Shortest Path Faster Algorithm)
下面讨论下SPFA
先转载这两篇,觉得写的不错,以后慢慢填坑
http://blog.csdn.net/hqd_acm/article/details/5442872
http://blog.csdn.net/hqd_acm/article/details/5804345
原文:http://www.cnblogs.com/clliff/p/4681424.html