一、最短路径简述
1、应用
从一个城市A到另一个城市B有很多的交通路线可以走,如果假设这些交通线路构成了一个无向图,则求从A到B的最短路径,可以用 广度优先算法 得到 这个 无向图 的 广度优先生成树,然后,从生成树中 找到 从A到B的路径 即为 从A到B的最短路径。
但是,现实世界远远没有这么简单,这些交通线路更像是一个 带权有向网,要求 从A到B的最短路径,需要考虑各个边上的权值。这个问题 可以用 最短路径算法 解决。
二、最短路径算法
1、求从某一个源点到其余各顶点的最短路径:迪杰斯特拉算法
迪杰斯特拉算法 的 核心思想是:
对于网络N=(V,E),将N中的顶点分成两组:
第一组S:已求出的最短路径的终点集合(初始时只包含源点v0)。
第二组V-S:尚未求出最短路径的终点集合(初始时为V-{v0})。
算法将按各顶点到v0间最短路径长度递增的次序,逐个将集合V-S中的顶点加入到集合S中去。在这个过程中,总保持从v0到S中顶点的距离不大于到V-S中顶点的距离。
理论:按照这种思路,则从v0到V-S中定点的最短路径:要么为<v0,v>,要么为中间经过S中顶点的一条路径。
反证:如果<v0,v>最短路径中历经的一个顶点不在S中,则说明存在一个顶点P,其到v0的距离 小于<v0,v>,这与S中顶点最短路径 总小于等于 V-S中最短路径 这一前提假设相违背,因此,我们可知,上述理论成立。
迪杰斯特拉算法 伪代码
//带权有向网 用 邻接矩阵 形式存储
//算法实现引入以下几个数据结构:
//S[i]为boolvalue,表示顶点i是否找到最短路径
//Path[i]代表v~0~到i的最短路径中,i的直接前驱结点;如果v~0~到i没有通路,Path[i]=-1
//D[i]表示i结点的最短路径长度
void ShortestPath_DIJ(AMGraph G,int v0){
//首先进行初始化 S[i],Path[i],D[i]
n=G.vexnum; //结点个数
for(i=0;i<n;++i){
S[i]=false;
D[i] = G.arcs[v0][i]; //初始化最短路径为 v0d到i的距离
if(D[i] < MAXInt){Path = v0;}
else{Path = -1;}
}
S[v0] = true;
D[v0] = 0;
//从n-1个结点中,依次找出最短路径,加到S中
for(i=1;i<n;++i){
min = MAXInt;
for(w=0;w<n;++w){
if(D[w]<min && !S[w]){
if(D[w]<min){
min=D[w];
v=w; //最小路径 结点
}
}
}
S[v] = true;
//将结点加入到S中后,更新V-S中结点最短路径值
for(i=0;i<n;++i){
if(!S[i] && G.arcs[v][i] + D[v] < D[i]){
D[i]= G.arcs[v][i] + D[v];
Path[i] = v;
}
}
}
}
总结:
1、算法在寻找n-1个顶点的最短路径时,有一个for(n-1),在这个for循环中,每次找一个最短路径,需要遍历所有的结点,因此,迪杰斯特拉算法执行的时间复杂度为O(n2)。即便将图采用邻接表的方式进行存储,算法时间复杂度依然为O(n2);
2、在实际中,人们往往想求得是从A到B的最短路径,即便如此,这个问题和
求源点到其它顶点的最短路径 一样复杂,也需要利用 迪杰斯特拉算法 实现,其时间复杂度仍然为O(n2);
3、在求得v0到各个顶点的最短路径后,要想找出vi的最短路径,则只需要追溯Path数组即可:vj=Path[vi]; vf=Path[vj]; v0=Path[vf];
2、每一对顶点之间的最短路径:佛洛依德算法
弗洛伊德算法 核心思想:
结点vi和vj之间的最短路径,最开始为G.arcs[vi][vj];
向vi和vj中逐个加入其它顶点,判断原来的最短路径和加入顶点的最短路径(<vi,…,vk> +<vk,…,vj>) 哪个更短,取更短的那个路径。
按照上述思路,求每对顶点间的最短路径。
佛洛依德算法伪代码
void ShortestPath_Floyd(AMGraph G){
//初始化各个顶点对间的最短路径,以及Path
for(i=0;i<G.vexnum;++i){
for(j=0;j<G.vexnum;++j){
D[i][j] = G.arcs[i][j]; //初始化最短路径
if(D[i][j] < MaxInt && i != j){Path[i][j] = i;}
else{Path[i][j] = -1;}
}
//逐个加入顶点,求解各个点对 的最短路径
for(k=0;k<G.vexnum;++k){
for(i=0;i<G.vexnum;++i){
for(j=0;j<G.vexnum;++j){
if(G[i][k] + G[k][j] < D[i][j]){
D[i][j] = G[i][k] + G[k][j];
Path[i][j] = Path[k][j];
}
}
}
}
}
总结:
1、佛洛依德算法的时间复杂度为O(n3);
2、假设要求D[1][2]的最短路径:Path[1][2]=3;Path[1][3]=4;Path[1][4]=1;则D[1][2]的最短路径为:1->4->3->2;