【图的最短路径】迪杰斯特拉算法求图的最短路径
要求:求带权有向图中某一结点到其他结点的最短路径。
用迪杰斯特拉算法求解,迪杰斯特拉算法书上的描述如下:
对于图G=(V,{E}),将图中的顶点归为两组:
第一组S:已求出的最短路径的终点集合(开始为{v0})
第二组:V-S尚未求出的最短路径的顶点的集合(开始为V-{v0}的全部顶点)
该算法将最短路径长度的递增顺序逐个将第二组中的顶点加入到第一组中,直到所有的顶点都被加入到第一组顶点集S为止。
这是书上的描述,比较抽象,本人将自己的理解与大家分享如下:
思路:
1首先初始化工作:三个准备数组path保存从v到i的路径,dist保存从v到i的最短路径,visit保存当前访问点是 已加入G中
2 初始化:若dist不为INFINITY则将v加入到path[i]中,将visit置false,将dist置能直达的距离
3然后将处顶点v外的其余n个顶点加入G中,所以应采用for循环n-1次,每次选取dist最小的顶点k加入G中
4加入后得重新求dist,因为k加入后可作中转站供原本不能走通的路径走通,一旦第i个顶点的dist重新修改后, 得将顶点k加入到i的path路径中
基于以上步骤代码如下:
#include<iostream> using namespace std; const int INFINITY=23678; const int M=3; /*typedef struct G { int ver[M]; int arc[M][M]; int vernum,arcnum; }G;*/ void path_(int n,int g[][M],int v)//为了简化过程,专注算法本质,将图的数据结构直接用数组加定点数来代替 { int dist[M],path[M],visit[M]; int k;//k用来保存每次选出的最小的路径的顶点的下标 for(int i=0;i<n;i++) { dist[i]=g[v][i]; if(dist[i]==INFINITY) { path[i]=0; } else { path[i]=v; } visit[i]=false;//刚开始所有的顶点都未加入G中 } visit[v]=true; for(int t=1;t<n-1;t++) { int min=INFINITY; for(int i=0;i<n;i++) { if(!visit[i]&&dist[i]<INFINITY) { min=dist[i]; k=i; } } if(min==INFINITY) return; visit[k]=true;//将顶点k加入已确定从v到i的最短路径的集合G中 for(int i=0;i<n;i++)//因为已将k加入到集合G中,所以k可以作为中转站来供其它原本不能直达的顶点可以走通 //所以必须将所有的(注意是所有的,这点类似图的DFS算法)顶点的最短路径进行修改 { if(!visit[i]&&dist[k]+g[k][i]<dist[i]) { dist[i]=dist[k]+g[k][i]; path[i]=k;//注意此时不要忘了,一旦修正则需将该中转点加入到该顶点的路径中 } } } for(int i=0;i<n;i++) { if(dist[i]==INFINITY) { cout<<"该顶点无最短路径"<<endl; } else { int pre=i; do{ printf("%d<--",pre); pre=path[pre];//注意这种技巧,类似链表,一环扣一环 }while(pre!=v); printf("%d",v); printf("\n"); } } } void main() { int vernum=3; int g[M][M]={{0,4,INFINITY},{12,0,5},{6,INFINITY,0}}; path_(vernum,g,2);//求每个顶点到顶点2的最短路径。 }
程序运行结果如下:
注:x<--y表示从y到x的最短路径,上图输出的是给定数组g[M][N]中的每个顶点到顶点2的最短路径。