两种经典的最短路径算法
1、迪杰斯特拉算法
算法思想:设置一个集合S记录已求得的最短路径的顶点,可以用数组s[]表示,初始化为0,当是s[vi]=1时,表示顶点vi放入S中,初始化时把v0放入S中。另外设置两个辅助数组
dist[]:记录顶点v0当其他顶点当前最短路径长度,dist[i]初始值为[v0][i];
path[]:path[i]表示从原点到顶点i之间的最短路径的前驱结点,在算法结束时,可以其值来追溯得到源点v0到vi之间的最短路径
算法步骤:
1、初始化:集合S初始化为{0},dist[]的初始值 dist[i] = edge[0][i],i=1,2,…n;
2、找出dist[]中最小值dist[j],将顶点j加入集合S,即修改s[vj] = 1;
3、修改从v0出发到集合V-S中任一顶点vk可到达的最短路径长度;如果dist[j] + edge[j][k] < dist[k],则令dist[k] = dist[j] + edge[j][k];
4、重复操作2、3步骤,操作n-1次,知道所有顶点都包含在S中
void dijkstra(MGraph G,int v,int dist[],int path[]){//v是源点下标
int s[Maxsize];
int min = 65535;//小技巧
int u;
//初始化s[],dist[],path[]
for(int i=0;i<G.vexnum;i++){
s[i] = 0;
dist[i] = edge[v][i];
if(G.edge[v][i]<min){
path[i] = v;//与源点连通的顶点path存源点下标
}
else{
path[i] = -1;//刚开始没有到源点路径的顶点path设置为-1
}
}
s[v]=1;//源点加入结合s
path[v]=-1;//源点不存在到自身的路径
//核心代码
for(int i=0;i<G.vexnum;i++){
//找到剩余顶点中距离最小的顶点u,并把它加入到最短路径
for(int j=0;j<G.vexnum;j++){
if(s[j]!= 1 && dist[j]<min){
min = dist[j];
u = j;//u保存当前找到最短路径的下标
}
}
s[u] = 1;//u是最小值,把它加入到最短路径
//新加入顶点u来判断是否找到新的最短路径,如果有则更新
for(int j=0;i<G.vexnum;j++){
if(s[j]!=1 && dist[u]+edge[u][j] < dist[j]){
dist [j] = dist[u]+edge[u][j];
path[j] = u;//由顶点u过来
}
}
}
}
算法分析:
核心部分是一个双重循环,这个双重循环的内循环两个并列的单重for循环组成,任意取其中一个循环中的操作作为基本操作,都可以得到时间复杂度为O(n的平方)
2、弗洛伊德算法
算法思想:
1、初始时,对于任意两个顶点vi到vj,若他们存在有向边,则以此边上的权值作为他们之间的最短路径长度;如他们之间不存在有向边,则以无穷大作为他们之间的最短路径长度。
2、逐步尝试在原路径中加入顶点k(k=0,1,…n-1)作为中间顶点,如果增加中间顶点后,得到的路径比原来的路径长度小,则更新此路径。
需要维护两个数组A,path,矩阵A用来记录当前任意两个顶点的最短长度。矩阵path用来记录当前两顶点间最短路径上要经过的中间顶点。
void Floyd(MGraph G,int path[][]){
int A[MaxSize][MaxSize];
//对A、path数组进行初始化
for(int i=0;i<G.vexnum;i++){
for(int j=0;j<G.vexnum;j++){
A[i][j] = G.edge[i][j];
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(A[i][k] + A[k][j] < A[i][j]){
A[i][j] = A[i][k] + A[k][j];
path[i][j] = k;
}
}
}
}
}
算法分析:
核心算法是三重循环,时间复杂度为O(n的三次方)
如果试题没有要求用哪种算法求最短路径,建议采用弗洛伊德算法,简洁明了。