最短路径问题之迪杰特斯拉算法

  在C语言的实训中,我学习到了一个自己以前曾经想学但是碍于水平不够未学习的算法:迪杰特斯拉算法。通俗地说,就是解决最短路径问题。相信大家都有过这样的经历:从a城市到b城市有一段距离,从a城市到c城市也有一段距离,从b,c城市到d城市都有一段距离,那么请问你要从a城市到d城市的最短距离是多少?相信只是那么几个城市大家口算都能完成,但是,如果城市一多呢?并且问你的最短距离的数目也不止一段呢?很明显,口算,心算就显得有些不够用了(天才排外),因此,掌握解决此类问题的算法就显得十分重要,下面,我将通过两道例题来讲解单向最短路径问题和双向最短路径问题。

例题1:单向最短路径问题

 

 

  在图中,我们可以明显地看到,直线左右的123456分别代表六个城市,直线上面的1234567代表从一个城市到另一个城市的距离,并且这种距离是单向的。如果要求从1城市到5城市的最短路径,相信大家一眼就能看出是从1城市直接到5城市的距离1,这似乎也有些太简单了,我们来分析一下:能到达5城市的有1城市和4城市,那么,其实到5城市的最短距离可以表示为p(5) = min(p(1) + d15,p(4) + d45),其中,p(1),p(4)分别代表从1城市到11城市和从1城市到4城市的最短距离,d15和d45分别代表城市1到城市5的距离和城市4到城市5的距离。同理,我们设t(i)为从1城市到i城市的临时距离,代表这个距离不一定是最短的距离,p(i)代表从1城市到i城市的最短距离,很明显,p(1)为0,t(i)(i = 2,3 ....)开始要设置为一个很大的数字,因此,我们可以得到一下公式:t(i) = min(p(j) + dji,p(k) + dki),p(i) = min(t(i),t(i + 1),t(i + 2).....t(6));由此,我们可以知道,我们只需要递推出t(i),再根据t(i)求出p(i)即可。在递推中,我们要注意,公式t(i) = min(p(j) + dji,p(k) + dki)中的k城市和i城市必须有直接路径。接下来上代码。

代码:

#include <stdio.h>
#define INF 999
int min(int a,int b)
{
return a < b ? a: b;
}
int main(void)
{
int p[7],l[7][7],T[7],i,j,flag,flag2[7][7] = {0},min1 = -1;
p[1] = 0;
l[1][3] = 6,l[3][4] = 3,l[4][5] = 4,l[4][6] = 7;
l[1][5] = 1,l[2][3] = 2;//将有直接路径的城市的距离表示出来
flag2[1][3] = 1,flag2[3][4] = 1,flag2[2][4] = 1,flag2[4][5] = 1,flag2[4][6] = 1;
flag2[1][5] = 1,flag2[2][3] = 1;//用flag数组表示两个城市是否有直接路径
for (i = 2;i < 7;i++)
{
T[i] = INF;
}
for (i = 2;i < 7;i++)
{
for (j = 1;j < i;j++)
{
if(flag2[j][i])
{
T[i] = min(T[i],p[j] + l[j][i]);//求出临时最短路径
}
}
for (j = i;j < 7;j++)
{
if(min1 == -1 || min1 > T[j])
{
min1 = T[j];
flag = j;
}
}
p[flag] = min1;//求出最短路径
min1 = -1;
}
//第一层大循环只是求得了从标号小的城市到标号大的城市的最短路径,因此还需要第二次大循环来求得真正的最短路径
for (i = 2;i < 7;i++)
{
for (j = 1;j < 7;j++)
{
if(flag2[j][i])
{
p[i] = min(p[i],p[j] + l[j][i]);
}
}
//需要特别注意的是,p[i]为999的值代表从1城市不能到达i城市
printf ("p[%d]=%d\n",i,p[i]);
}
printf ("%d\n",p[6]);

return 0;
}

例题2:双向最短路径问题

如图:

 

 

图略大,还请见谅,其实还是上面那一张图,不过没了箭头,也就是既可以从1城市到5城市,也可以从5城市到1城市,那么同理,其实我们只需要在上面的代码中加上从5城市到1城市的距离,以及其他可以互通的城市的距离,并且用flag数组表示他们有直接路径,废话不多少,直接上代码。

代码:

#include <stdio.h>
#define INF 999
int min(int a,int b)
{
return a < b ? a: b;
}
int main(void)
{
int p[7],l[7][7],T[7],i,j,flag,flag2[7][7] = {0},min1 = -1;
p[1] = 0;
l[1][3] = 6,l[3][4] = 3,l[4][5] = 4,l[4][6] = 7;
l[1][5] = 1,l[2][3] = 2;//将有直接路径的城市的距离表示出来
l[3][1] = 6,l[4][3] = 3,l[5][4] = 4,l[6][4] = 7;
l[5][1] = 1,l[3][2] = 2;//将有直接路径的城市的距离表示出来
flag2[1][3] = 1,flag2[3][4] = 1,flag2[4][5] = 1,flag2[4][6] = 1;
flag2[1][5] = 1,flag2[2][3] = 1;//用flag数组表示两个城市是否有直接路径
flag2[3][1] = 1,flag2[4][3] = 1,flag2[5][4] = 1,flag2[6][4] = 1;
flag2[5][1] = 1,flag2[3][2] = 1;//用flag数组表示两个城市是否有直接路径
for (i = 2;i < 7;i++)
{
T[i] = INF;
}
for (i = 2;i < 7;i++)
{
for (j = 1;j < i;j++)
{
if(flag2[j][i])
{
T[i] = min(T[i],p[j] + l[j][i]);//求出临时最短路径
}
}
for (j = i;j < 7;j++)
{
if(min1 == -1 || min1 > T[j])
{
min1 = T[j];
flag = j;
}
}
p[flag] = min1;//求出最短路径
min1 = -1;
}
//第一层大循环只是求得了从标号小的城市到标号大的城市的最短路径,因此还需要第二次大循环来求得真正的最短路径
for (i = 2;i < 7;i++)
{
for (j = 1;j < 7;j++)
{
if(flag2[j][i])
{
p[i] = min(p[i],p[j] + l[j][i]);
}
}
//需要特别注意的是,p[i]为999的值代表从1城市不能到达i城市
printf ("p[%d]=%d\n",i,p[i]);
}
printf ("%d\n",p[6]);

return 0;
}

 

posted @ 2017-07-06 22:27  505算法小菜`  阅读(2684)  评论(0编辑  收藏  举报