最短路径算法在生活中有极其重要的意义与运用,如高铁、地铁的路线规划问题,最近学习了迪杰斯特拉算法,颇有感慨,遂成一篇笔记以记之。
以邻接矩阵为存储结构,如下图所示:
拓扑结构图如下:
求v0到v8的最短路径,其实思想比较简单,蕴含着贪心法的思想,它并不是一下子求出v0到v8的最短路径,而是先求出v0到v1的最短路径,再求出v2到v3的最短路径,过程就是基于已经求出的最短路径的基础上,求出更远拓扑点的最短路径,最终得出结果;
初始时d,p,final数组的值如下:
d: 0 1 5 65535 65535 65535 65535 65535 65535
p: 0 0 0 0 0 0 0 0 0
final: 1 0 0 0 0 0 0 0 0
代码如下:
#define MAXVEX 9
#define INFINITY 65535 //定义两个宏,主要是便于以后修改数据
typedef int Patharc[MAXVEX];//用于存储最短路径下标的数组
typedef int ShortPathTable[MAXVEX];//用于存储到各点的最短路径权值之和
void shortpath_dj(Mgraph g,int v0,Patharc *p,ShortPathTable *d)
{
int v,w,k,min;
int final[MAXVEX];//final[w]表示已经求得顶点V0到Vw的最短路径,其实就是一个标记数组;
//初始化数据
for(v=0;v<.num;v++)
{
final[v]=0; //全部顶点初始化为未找到最短路径;
(*d)[v]=g.arc[v0][v]; //将和v0点有连线的顶点加上权值;这时初始化完成之后*d指向的数组内容即为邻接表的第一行内容,图中的无穷大全部用65535替代;
(*p)[v]=0; //初始化路径数组p为0;
}
(*d)[v0]=0; //v0到v0的的路径为0;
final[v0]=1; //v0到v0不需要求路径;
//开始主循环,每次求出v0到某一个顶点的最短路径;
for(v=1;v<g.num;v++)
{
min=INFINITY;
for(w=0;w<g.num;w++)
{
if(!final[w]&&(*d)[w]<min)
{
k=w;
min=(*d)[w];
}
}
final[k]=1; //表示目前找到的最近顶点下标所对应的内容置1,即代表找到了v0到该点的最短路径;
}
//修正当前的最短路径以及距离;
for(w=0;w<g.num;w++)
{
//如果经过v顶点的路径比现在这条路径的长度短时,就将其覆盖;
if(!final[w]&&(min+g.arc[k][w]<(*d)[w]))
{
(*d)[w]=min+g.arc[k][w]; //修改当前路径的长度,其实(*d)就是一个临时数组,用来不断去更好地取值,这也是贪心法的表现,也可以说这个数组喜新厌旧哈;
(*p)[w]=k; //*p你也可以理解为是用来存放前驱结点的数组,具有双重含义,就像现实生活中人们说话有时也有两层意思差不多。比如(*p)[2]内容为1,就表示v2顶点的前驱是v1!
}
}
}
每次循环之后,三个数组的值都会相应的变化,最终d,p,final三个辅助数组的值如下,大家可以顺着程序推演一下,很容易就可以推出,也将很好的理解dj算法原理:
d: 0 1 4 7 5 8 10 12 16
p: 0 0 1 4 2 4 3 6 7
final: 1 1 1 1 1 1 1 1 1
看似复杂的算法也就是通过一步步的组合而成,仔细分析就可得其精髓。