最短路径的两种算法

最短路径问题的简单描述:
有N个城市,每两个城市之间有一个权值(i –>j 和 j –>i 不同),求两个城市之间权值最小的路径。

存储格式:
1、我们需要一个N*N的数组e[N][N]来存储各个城市之间的权值,e[i][j]表示从i 到 j 的权值。
2、#define inf 99999999 //定义一个我们认为正无穷的值表示两个城市之间不直接相连。
3、城市自己到自己的路径用0表示。

一、震惊!只有五行的算法–Floyd Warshall算法

弗洛伊德算法是基于动态规划的思想,其所求的解是所有任意两个城市之间的最短路径,所以称为多源最短路径。
弗洛伊德算法的时间复杂度是O(n^3)

想一想:如果要让任意两点(例如从顶点a到顶点b)之间的路径变短,只能引入第三个点(顶点k),并通过这个顶点k中转,即a->k->b,才有可能缩短原从顶点a到顶点b的路程。那么这个中转的顶点k是1-N中哪个点呢?甚至有时候不只过一个点,而是经过两个点或者更多的点中转会更短,即a->k1->k2->…->b。那么其实,每个顶点都有可能使得另外两个顶点之间的路程变短。我们将这个问题一般化:
1、当任意两点之间不允许经过第三个点时,这些城市之间的最短路径就是初始路程。
2、假如现在只允许经过1号顶点,求任意两点之间的最短路程,只需要判断e[i][1]+e[1][j]是否比e[i][j]要小即可。其中i是1-N循环,j也是1-N循环。代码:

for(i = 1;i <= N;i++)
    for(j = 1;j <= n;j++ )
    {
        if(e[i][j] > e[i][1]+e[1][j])
            e[i][j] = e[i][1]+e[1][j];
    }

3、接下来求只允许经过1和2号两个顶点的情况下任意两点之间的最短路程。我们只需要在只允许经过1号顶点时任意两点的最短路程的基础下,再判断如果经过2号顶点是否可以使得i 到 j 的路程更短。即判断e[i][2]+e[2][j]是否比e[i][j]要小。

//经过1号顶点
for(i = 1;i <= N;i++)
    for(j = 1;j <= n;j++ )
    {
        if(e[i][j] > e[i][1]+e[1][j])
            e[i][j] = e[i][1]+e[1][j];
    }

//经过2号顶点
for(i = 1;i <= N;i++)
    for(j = 1;j <= n;j++ )
    {
        if(e[i][j] > e[i][2]+e[2][j])
            e[i][j] = e[i][2]+e[2][j];
    }

4、同理,继续在只允许经过1,2,3号顶点进行中转的情况下,求任意两点之间的最短路程。
… …
最后允许通过所有顶点作为中转。
整个算法实现起来非常简单,核心代码只有五行:

for(k = 1;k <= n;k++)
    for(i = 1;i <= N;i++)
        for(j = 1;j <= n;j++ )
        {
            if(e[i][k] < inf && e[k][j] < inf && e[i][j] > e[i][k]+e[k][j] )
            //防止定义的无穷数过大相加越界,可以添加两个判定条件
            e[i][j] = e[i][k]+e[k][j];
        }

二、单源最短路径的算法–Dijkstra算法

Dijkstra算法也是基于动态规划的思想,其求出的是单源最短路径,即一个顶点到其他各个顶点的最短路径,那么此处我们以1号顶点为例。
注意,除了二维数组e来存储边之间的关系外,我们还需要一个辅助数组dis[N]来存储1号顶点到其余各个顶点的初始路程。
此处我们引入样例数组来使得说明的更清晰。
样例数组:
| 0 | 2 | 6 | 4
| ~ | 0 | 3 | ~
| 7 | ~ | 0 | 1
| 5 | ~ | 12 | 0
这是一个4*4的数组,表示4个城市之间的连通状态,自己到自己为0,~表示不可达。
初始dis[4] = | 0 | 2 | 6 | 4 |
算法思想:
1、既然是求1号顶点到其余各个顶点的最短路径,那就先找一个离1号顶点最近的顶点。通过数组dis可知当前离1号顶点最近的是2号顶点。当选择了2号顶点后,dis[2]的值就已经从“估计值”变为了“确定值”,即1号顶点到2号顶点的最短路径就是当前dis[2]的值。为什么呢?(想一想:目前离1号顶点最近的就是2号顶点,而且这个图所有的边都是正数,那么肯定不可能通过第三个顶点中转,使得1号顶点到2号顶点的路程进一步缩短了,因为1号顶点到其他顶点的路程肯定没有1号到2号顶点短。)
2、既然选了2号顶点,接下来再来看2号顶点有哪些边呢?有2->3和2->4这两条边。先讨论通过2->3这条边能够让1号顶点到3号顶点的路程变短,也就是说现在来比较dis[3]和dis[2]+e[2][3]的大小。其中dis[3]表示1号顶点到3号顶点的路程,dis[2]+e[2][3]就表示从1号顶点先到2号顶点,再通过2->3这条边,到达3号顶点。
待续。

你可能感兴趣的:(算法,学习笔记)