5.4.2图的应用(最短路径)
迪杰斯特拉:
用集合S来记录以求得最短路径得顶点,可以用一个数组来是s[]来实现,初始化为0,当s[vi]=1时表示vi加入S中,初始化把源点v0放入S中。此外,在构造过程中还设置了两个辅助数组:
dist[]:记录了从源点到顶点i之间的最短路径长度,dist[]初值为arcs[v0][i]
。
path[]:记录path[i]表示从源头到顶点i之间得最短路径得前驱结点,在算法结束时,可根据其值追溯到源点v0到vi最短路径。
算法思想:
假设从顶点0出发 ,也就是顶点V0为源点,集合S最初只包含顶点0,邻接矩阵Arcs表示带权有向图,Arcs[i][j]
表示有向边<i,j>权值,若不存在有向边<i,j>,则arcs[i][j]
=无穷。
算法步骤:
1)初始化:集合S初始为{0},dist[]得初始值dist[i]=arcs[0][i]
,i=0,1,2,…n-1。
2)找出dist[]中最小的值dist[j]将顶点j加入集合S,即修改s[vj]=1。
3)修改从vo出发到集合V-S上任意顶Vk可达得最短路径长度:如果dist[j]+arcs[j][k]
<dist[k],则dist[k] =dist[j]+arcs[j][k]
,另外更新path[k]=j。
4)重复2)3)操作n-1次,直到所有的顶点都包含在S中。
由于只剩顶点6所以直接加入顶点6并修改s[6]=1。
算法代码:
void Dijikstra(MGraph G ,int path[],int dist[]){//v是源点下标
int s[MaxSize];
int i,j,min,u;
for(i=0;i<G.vexnunm;i++){
dist[i]=G.edge[v][i];
s[i]=0;
if(G.Edge[v]<65535)
path[i]=v;
else
path[i]=-1;
}
s[v]=1; //源点加入集合S
path[v] =-1;//源点不存在到自身的路径
//@1:内层第一个循环是找到剩余顶点中距离最小的顶点u并把它加入最短路径
//@2:内层第二个循环是由新加入的顶点u来判断是否找到了新的更新路径,如果有就操作,没有就不操作
for(i=0;i<G.vexnum;i++){
min =65535;
for(j=0;j<g.vexnum;j++){
if(s[j]==0&&dist[j]<min){
u=j;
min=dist[j];
}
}
s[u]=1; //到u的距离最短,所以顶点u加入最短路径
for(j=0;j<G.vexnum;j++){
if(s[j]==0&&dist[u]+G.Edege[u][j]<dist[j]){
dist[j]=dist[u]+G.Edege[u][j];
path[j]=u;//这条较短的路径是由顶点u过来的
}
}
}
}
时间复杂度:
核心部分有一个双重循环,这个双重循环的内循环又是两个并列的单重for循环组成(找距离最小点和更新距离),所以时间复杂度为O(n^2)其中n为图中定点数。
note:迪杰斯特拉算法不能用于权值有负数的图,不然会出错。
弗洛伊德(多源点)
求图中任意一对顶点间的最短路径
算法思想:
递推产生一个n阶方阵序列A(-1),A(0),A(1),…A(n-1)
其中A(K)[i][j]
表示任意顶点vi和vj的路径长度,k表示绕行第k个顶点的运算步骤。
初始时,对于任意两个顶点vi和vj,若它们之间存在边,则此边上第权值作为他们之间的最短路径;若不存在,则以无穷作为它们之间的最短路径长度,以后逐步尝试在原路径中加入顶点k(k=0,1,2,…n-1)作为中间结点。如果增加中间结点后,得到的路径比原来的路径长度减少了,则以此新路径代替原路径。
维护两个矩阵 A(记录两个点之间的最短路径) 和Path(记录两个顶点将最短路径上要经过的中间结点)
note:每一轮,我们选取那个顶点为中间结点时,不需要考虑它所在的行和列还有主对角线上的最短路径的变化。
执行完毕。
代码:
void Floyd(MGraph G,int Path[][]){
int i,j,k;
int A[MaxSize][MaxSzie];
for(i=0;i<G.vexnums;i++){
for(j=0;j<G.vexnums;j++){
A[i][j]=G.Edges[i][j];
Path[i][j]=-1;
}
}
for(k=0;k<G.vexnums;k++){
for(i=0;k<G.vexnums;i++){
for(j=0;k<G.vexnums;j++){
if(A[i][j]>A[i][k]+A[k][j]){
A[i][j]=A[i][k]+A[k][j];
Path[i][j]=k;
}
}
}
}
}
时间复杂度:核心为三重循环所以时间复杂度为O(n^3),n是顶点数。
note:迪杰斯特拉算法和弗洛伊德算法时间复杂度比较,由于迪杰斯特拉时单源点,而弗洛伊德为多源点所以迪杰斯特拉时间复杂度为O(n^2),如果要求所有顶点当作源点,需要*O(n),所以说两个算法的时间复杂度差不多。