最短路
最短路,顾名思义就是对于一个连通图求起点到终点的最短距离:
①Floyd:
这是利用动态规划思想求得任意两点间的最短路,很简单;
用dp[i][j]记录i到j的最短路长度,枚举i和j,再枚举一个k,若i到k的当前最短路与j到k的当前最短路之和大于i到j的当前最短
路,则更新dp[i][j],即dp[i][j]=max(dp[i][j],dp[i][k]+dp[k][j]),主程序段如下:
for (int i=1;i<=n;i++) for (int j=i+1;j<=n;j++) for (int k=1;k<=n;k++) dp[i][j]=max(dp[i][j],dp[i][k]+dp[k][j]);
时间复杂度:O(n^3),并不高效。
②SPFA:
这是采用从起点出发,向外不断扩展更新的方法;
先维护一个队列,开始时起点入队,每次从队列中拿出一个点i,枚举它的所有边,若从起点到i的当前最短路与此边之和大
于起点到该边另一点的当前最短路,则更新之,再放进队列,主程序段如下:
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) returnfalse; if(!dq.empty()){ if(dist[to]>dist[dq.front()]) dq.push_back(to); else dq.push_front(to); } else dq.push_back(to); } } } }
期望的时间复杂度:O(k*n^2), 其中k为所有顶点进队的平均次数,n为总顶点数,可以证明k一般小于等于2。 SPFA还有
SLF,LLL,滚动数组等优化。
③Dijkstra:
这是按路径长度递增次序产生最短路;
把顶点集合V分成两组:
(1)S:已求出的顶点的集合(初始时只含有源点V0);
(2)V-S=T:尚未确定的顶点集合;
将T中顶点按递增的次序加入到S中,保证:
(1)从源点V0到S中其他各顶点的长度都不大于从V0到T中任何顶点的最短路径长度;
(2)每个顶点对应一个距离值;
S中顶点:从V0到此顶点的长度;
T中顶点:从V0到此顶点的只包括S中顶点作中间顶点的最短路径长度;
依据:可证V0到T中顶点Vk的,或是从V0到Vk的直接路径的权值;或是从V0经S中顶点到Vk的路径权值之和(反证法
可证);
另外,据说可以用堆优化(可惜我还不太会),主程序段如下:
while(count<n) { tempmin=INFINITE; for(i=1;i<=n;i++) { if(in[i]==0&&Len[i]<tempmin) //find the smallest one { tempmin=Len[i]; si=i; } } in[si]=1; count++; for(i=1;i<=n;i++) //updata the length { if(in[i]==0&&(tempmin+mGraph.matrix[si][i])<Len[i]) { Len[i]=tempmin+mGraph.matrix[si][i]; } } }
时间复杂度为:O(n^2),堆优化的话时间复杂度为:O((m+n)logn)。我已开始了解它的基本思想后自己竟打出了个SPFA
的说,个人觉得它是SPFA的优化版,只不过加了个每次枚举时当前未标记的点中路径最短的即为此点的最短路,然后就不用
每次把它放入队列了,时间复杂度也就少了个k,个人觉得。
它是目前已知求单源最短路效率最高的一种,若求的是全源最短路,就对每一个点做一次,时间复杂度为O(n^3),事实上
也就和Floyd一样。
④Bellman-Ford:
这是求含负权图的单源最短路径算法;
进行不停地松弛(原文是这么写的,为什么要叫松弛,争议很大),每次松弛把每条边都更新一下,若n-1次松弛后还能更
新,则说明图中有负环,无法得出结果,否则就成功完成,主程序段如下:
for(int i = 1; i <= nodenum; i++) //初始化 dis[i]= (i == original ? 0 : MAX); for(inti = 1; i <= nodenum - 1; i++) for(intj = 1; j <= edgenum; j++) if(dis[edge[j].v]> dis[edge[j].u] + edge[j].cost) //松弛(顺序一定不能反~){ dis[edge[j].v]= dis[edge[j].u] + edge[j].cost; pre[edge[j].v]= edge[j].u; } boolflag = 1; //判断是否含有负权回路 for(inti = 1; i <= edgenum; i++) if(dis[edge[i].v]> dis[edge[i].u] + edge[i].cost){ flag= 0; break; }
时间复杂度为O(nm),其中n为点数,m为边数