最短路径问题是图论研究中的一个经典算法问题, 旨在寻找图(由结点和路径组成的)中两结点之间的最短路径。 算法具体的形式包括:
确定起点的最短路径问题 - 即已知起始结点,求最短路径的问题。适合使用Dijkstra算法。
确定终点的最短路径问题 - 与确定起点的问题相反,该问题是已知终结结点,求最短路径的问题。在无向图中该问题与确定起点的问题完全等同,在有向图中该问题等同于把所有路径方向反转的确定起点的问题。
确定起点终点的最短路径问题 - 即已知起点和终点,求两结点之间的最短路径。
全局最短路径问题 - 求图中所有的最短路径。Floyd-Warshall算法
这个算法是通过为每个顶点 v 保留目前为止所找到的从s到v的最短路径来工作的。初始时,原点 s 的路径长度值被赋为 0 (d[s] = 0),同时把所有其他顶点的路径长度设为无穷大,即表示我们不知道任何通向这些顶点的路径(对于 V 中所有顶点 v 除 s 外d[v] = ∞)。当算法结束时,d[v] 中储存的便是从 s 到 v 的最短路径,或者如果路径不存在的话是无穷大。 Dijkstra 算法的基础操作是边的拓展:如果存在一条从 u 到 v 的边,那么从 s 到 v 的最短路径可以通过将边(u, v)添加到尾部来拓展一条从 s 到 u 的路径。这条路径的长度是 d[u] + w(u, v)。如果这个值比目前已知的 d[v] 的值要小,我们可以用新值来替代当前 d[v] 中的值。拓展边的操作一直执行到所有的 d[v] 都代表从 s 到 v 最短路径的花费。这个算法经过组织因而当 d[u] 达到它最终的值的时候每条边(u, v)都只被拓展一次。
算法维护两个顶点集 S 和 Q。集合 S 保留了我们已知的所有 d[v] 的值已经是最短路径的值顶点,而集合 Q 则保留其他所有顶点。集合S初始状态为空,而后每一步都有一个顶点从 Q 移动到 S。这个被选择的顶点是 Q 中拥有最小的 d[u] 值的顶点。当一个顶点 u 从 Q 中转移到了 S 中,算法对每条外接边 (u, v) 进行拓展。
http://baike.baidu.com/view/4719095.htm
dijkstra算法思想:
开始时,S={u},T=V-{u}; 对T中所有顶点x,如果u到x存在边,置d(u,x)=c(u,x); 否则d(u,x)=无穷大。然后,对T中所有顶点x,
寻找d(u,x)最小的顶点t,即:
d(u,t)=min(d(u,x)|x属于T)
则d(u,t)就是顶点t到顶点u的最短路径距离。同时,顶点t也是集合T中所有顶点距离u最近的顶点。把顶点t从T中删除,把它并入S。然后对T中与t相邻接的所有顶点x,用下面的公式更新d(u,x)的值:
d(u,x)=min(d(u,x), d(u,t) +c(t,x);
继续上面的步骤,一直到T空为止。
类似代码实现:
#include<iostream> using std::iostream; //不能用using namepace std;应为utility中有一个函数 prev,在传递参数prev,编译错误不明确 using std::cout; using std::cin; using std::endl; const int maxnum = 100; const int maxint = 999999; //各数组都从下标1开始 int dist[maxnum]; //表示当前点到原点的最短路径长度 int prev[maxnum]; //记录当前点的前一个结点 int c[maxnum][maxnum]; //记录图的两点间路径长度 int n,line; //图的结点数和路径数 // n -- n nodes // v -- the source node // dist[] -- the distance from the ith node to the source node // prev[] -- the previous node of the ith node // c[][] -- every two nodes' distance void Dijstra(int n,int v,int *dist,int *prev,int c[maxnum][maxnum]) { bool s[maxnum]; //是否应该存到S集合中 for(int i=1;i<=n;i++) { dist[i]=c[v][i]; s[i]=0; //初始都未用过该点 if(dist[i]==maxint) prev[i]=0; else prev[i]=v; } dist[v]=0; s[v]=1; // 依次将未放入S集合的结点中,取dist[]最小值的结点,放入结合S中 // 一旦S包含了所有V中顶点,dist就记录了从源点到所有其他顶点之间的最短路径长度 // 注意是从第二个节点开始,第一个为源点 for(int i=2;i<=n;i++) { int tmp=maxint; int u=v; //找出当前未使用的点J 的dist[j]的最小值 for(int j=1;j<=n;j++) if(!s[j]&&dist[j]<tmp) { u=j; // u保存当前邻接点中距离最小的点的号码 tmp=dist[j]; } s[u]=1; //表示点u已存入S集合中 //跟新dist for(int j=1;j<=n;j++) if((!s[j])&&c[u][j]<maxint) { int newdist=dist[u]+c[u][j]; if(newdist<dist[j]) { dist[j]=newdist; prev[j]=u; } } } } //查找从原点v到终点u的路径,并输出 void searchPath(int *prev,int v,int u) { int que[maxnum]; int tot=1; que[tot]=u; tot++; int tmp=prev[u]; while(tmp!=v) { que[tot]=tmp; tot++; tmp=prev[tmp]; } que[tot]=v; for(int i=tot;i>=1;--i) if(i!=1) cout<<que[i]<<"->"; else cout<<que[i]<<endl; } int main() { freopen("dijkstra.txt","r",stdin); //各数组下标都从1开始 //输入结点数, cin>>n; //输入路径数 cin>>line; int p,q,len; //输入p ,q两点及其路径长度 //初始化c[][]为maxint for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) c[i][j]=maxint; for(int i=1;i<=line;++i) { cin>>p>>q>>len; if(len<c[p][q]) //有重边 { c[p][q]=len; //p指向q c[q][p]=len; //q指向p,这样表示无向图 } } for(int i=1;i<=n;++i) dist[i]=maxint; /* for(int i=1;i<=n;++i) { for(int j=1;j<=n;j++) printf("%8d",c[i][j]); printf(" "); } */ Dijstra(n,1,dist,prev,c); //最短路径长度 /* cout<<"源点到最后一个顶点的最短路径长度: "<<dist[n]<<endl; //路径 cout<<"源点到最后一个顶点的路径为: "<<endl; */ cout<<"顶点a到个顶点的最短路径为"<<endl; for(int i=2;i<=n;i++) { searchPath(prev,1,i); } /* searchPath(prev,1,2); searchPath(prev,1,3); searchPath(prev,1,n); */ }
参考:http://www.wutianqi.com/?p=1890