详解最短路径中的迪杰斯特拉算法
0x01.关于迪杰斯特拉算法
迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。这个算法不适用于含负权值的图。
0x02.最短路径
最短路径就是图上两个顶点之间要连通,怎样连通花费的代价最小,即所有路径上的权值和最小。
说明:下图这种情况是存在的,也就是说,两点间,线段不一定最短。(这个权值只是一个有特定意义的数字)
0x03.基础代码实现
经典算法先上代码。
/*
参数说明:
V0为路径的起始点,是顶点下标
P[V]用来储存路径,存储的都是顶点下标
D[v]表示V0到V的最短路径长度
*/
void ShortPath_Dijkstra(Graph G, int V0, int* P, int* D)
{
int v, w, k, min;
int final[MAXSIZE];//记录顶点的最短路径是否求出
for (v = 0; v < G.numv; v++)//初始化
{
final[v] = 0;
D[v] = G.edge[V0][v];//将与V0连线的顶点加上权值
P[v] = 0;
}
D[V0] = 0;//表示V0到自身的路径为0
final[V0] = 1;//起始点不需要求路径
for (v = 1; v < G.numv; v++)
{
min = INTMAX;//先设为最大值,目的是找最小值
for (w = 0; w < G.numv; w++)
{
if (!final[w] && D[w] < min)
{
k = w;//k来记录最小值的下标
min = D[w];
}
}
final[k] = 1;//将找到的顶点置为1
for (w = 0; w < G.numv; w++)//找相邻点的最短路径,并不断更新最短路径长度
{
if (!final[w] && (min + G.edge[k][w] < D[w]))
{
D[w] = min + G.edge[k][w];
P[w] = k;
}
}
}
}
0x04.过程详解
先看一个网图:
初始化完成后,开始第一个大循环,然后开始第一个小循环,此循环的目的是找出D数组中的最小值,此时的D数组为[0,5,9,32767,32767,32767,32767,32767,32767],0不参与讨论,则最小值min=5,k=1,然后final[1]=1,此时的final为[1,1,0,0,0,0,0,0,0]。然后第二个小循环,第二个循环的内容是,逐个对下标为k=1的顶点的邻接边的权值加上min与D数组中相应位置进行比较,这个循环过后,D为[0,5,9,16,14,32767,32767,32767,32767],P为[0,0,0,1,1,0,0,0,0]。此时V1的最短路径已确定。
第二个大循环,第一个小循环,此时的min=9,k=2,final[2]=1,接着进入第二个小循环,开始比较V2的邻接边,循环结束后,D为[0,5,9,16,14,20,32767,32767,32767],此时的P为[0,0,0,1,1,2,0,0,0],此时的final为[1,1,1,0,0,0,0,0,0]。此时到V2的最短路径已确定为9。
第三个大循环,第一个小循环,找到min=14,k=4,final[4]=1,此时的final为[1,1,1,0,1,0,0,0,0],第二个小循环后,D为[0,5,9,16,14,20,24,27,32767],P为[0,0,0,1,1,2,4,4,0]。此时到V4的最短路径确定为14。
第四个大循环,第一个小循环,找到min=16,k=3,此时的final为[1,1,1,1,1,0,0,0,0],第二个小循环后,D为[0,5,9,16,14,20,23,27,32767],此时的P为[0,0,0,1,1,2,3,4,0]。此时V3的最短路径确定为16。
第五个大循环,第一个小循换,找到min=20,k=5,此时的final为[1,1,1,1,1,1,0,0,0],第二个小循换后,D为[0,5,9,16,14,20,23,27,32767],此时的P为[0,0,0,1,1,2,3,4,0]。此时V5的最短路径确定为20。
第六个大循环,第一个小循换找到min=23,k=6,此时的final为[1,1,1,1,1,1,1,0,0],第二次小循换后D为[0,5,9,16,14,20,23,27,34],此时的P为[0,0,0,1,1,2,3,4,6],此时到V6的最短路径确定为23。
第七个小循环,第一个小循环确定min-27,k=7,此时的final为[1,1,1,1,1,1,1,1,0],第二次小循环后D为[0,5,9,16,14,20,23,27,34],P为[0,0,0,1,1,2,3,4,6]此时到V7的最短路径确定为27。
最后一次循环,确定到V8的最短路径为34,此时的D为[0,5,9,16,14,20,23,27,34],这里面数字的含义是起始点到下标位置的顶点的最短路径,P为[0,0,0,1,1,2,3,4,6],这里面每个数字的含义是到这个顶点的最短路径的上一个点的下标值,比如,从起始到V8,应该这样看,它的上一个点是V6,V6的上一个点是V3,V3的上一个点是V1,V1的上一个点是V0,所以可以将V0到V8的最短路径确定下来。如下图:
0x05.原理详解
迪杰斯特拉算法的原理其实就是,先找到相邻的最短路径,然后再利用这个路径,一直往后寻找,一直到需要的点的路径。
D数组存储的是各个顶点到起始点的最短路径,从起始点开始,每次都把最小值加上对应顶点的权值与D数组中的值比较,在这样的遍历中,充分考虑到了各种可能的路径,每做一次大循环,就找到一个最小值,不断的更新D数组中的值,循环结束后,D数组中的值就是最终最短路径。
P数组起到了一个记录路径的功能,能把这条路径的上一个顶点的值记录下来,只要不断回溯,就能找到最终得路径。
final数组起到了记录到某个顶带的最短路径是否找到的功能。
如果要求任意两个顶点的最短路径,可以对每个顶点作为起始点再来依次循环,等于再加上一个大循环。
时间复杂度为O().