单源最短路径
单源最短路径问题可以用迪杰斯特拉算法解决,具体步骤与普利姆算法类似,不同的是在最短路径问题中加入了辅助数组中储存每个点的直接前驱和当前的最短路径长度,以及判断每个点是否被加入路径中。
迪杰斯特拉算法的具体步骤如下
(1) 初始时,S只包含起点s;U包含除s外的其他顶点,且U中顶点的距离为"起点s到该顶点的距离"[例如,U中顶点v的距离为(s,v)的长度,然后s和v不相邻,则v的距离为∞]。
(2) 从U中选出"距离最短的顶点k",并将顶点k加入到S中;同时,从U中移除顶点k。
(3) 更新U中各个顶点到起点s的距离。之所以更新U中顶点的距离,是由于上一步中确定了k是求出最短路径的顶点,从而可以利用k来更新其它顶点的距离;例如,(s,v)的距离可能大于(s,k)+(k,v)的距离。
(4) 重复步骤(2)和(3),直到遍历完所有顶点。
具体的实现为(以邻接矩阵法表示图为例)
//三个辅助数组
int S[mvnum];//记录从顶点到某点是否已经确定最短路径长度
int D[mvnum];//记录从源点到某点的当前最短路径长度。若源点到该点有弧,则值为弧的权值,否则则为maxint
int Path[mvnum];//记录当前点的直接前驱顶点序号,没有则为-1
void dij(AMGraph G,int v0)//思想与普利姆算法类似
{
int v;
for(v=0;v<G.vexnum;v++)
{
D[v]=G.arcs[v0][v];
if(D[v]<maxint)
Path[v]=v0;//若该点与v0之间有边,则记录前驱顶点为v0
else
Path[v]=-1;//否则为-1
}
S[v0]=1;//将v0加入数组中
D[v0]=0;
for(int i=1;i<G.vexnum;i++)//循环n-1次找边
{
int min=maxint;
for(int w=0;w<G.vexnum;w++)//求出与该点连接的边的最小权值
{
if(!S[w]&&D[w]<min)
{
v=w;
min=D[w];
}
}
S[v]=1;//将v点加入路径中
for(int w=0;w<G.vexnum;w++)//更新当前顶点与其他顶点间的最短距离
{
if(!S[w]&&(D[v]+G.arcs[v][w]<D[w]))
{
D[w]=D[v]+G.arcs[v][w];
Path[w]=v;
}
}
}
}
每对顶点间的最短路径
求每对顶点间的最短路径时可以使用n次迪杰斯特拉算法,也可以使用弗洛伊德算法解决,二者的时间复杂度均为O(),但弗洛伊德算法形式上更为简单优美,思路上更为简单
弗洛伊德算法也需要借助两个辅助数组完成
int D[mvnum][mvnum];//记录某点的当前最短路径长度。若源点到该点有弧,则值为弧的权值,否则则为maxint
int Path[mvnum][mvnum];//记录当前点的前一顶点序号,没有则为-1
在寻找每对顶点之间的最短路径时,遍历整个D数组查看每一对顶点,再添加一个循环看每对顶点间是否存在加一个点后更短的路径即可
void floyd(AMGraph G)//思想与普利姆算法类似
{
//初始化D和Path
for(int i=0;i<G.vexnum;i++)
{
for(int j=0;j<G.vexnum;j++)
{
D[i][j]=G.arcs[i][j];
if(i!=j&&D[i][j]<maxint)
Path[i][j]=i;
else
Path[i][j]=-1;
}
}
for(int k=0;k<G.vexnum;k++)
{
for(int i=0;i<G.vexnum;i++)
{
for(int j=0;j<G.vexnum;j++)
{
if(D[i][k]+D[k][j]<D[i][j])//若存在比直接到达更短的路径,则更改D[i][j]
{
D[i][j]=D[i][k]+D[k][j];
Path[i][j]=Path[k][j];
}
}
}
}
}