数据--第45课 - 最短路径

第45课 - 最短路径

1. 最短路径

如果从有向图中某一顶点(称为源点)到达零一顶点(称为终点)的路径可能不止一条,如何找到一条路径使得沿此路径上各边上的权值总和达到最小。

问题解决:

单源最短路径问题:Dijkstra算法。

所有顶点之间的最短路径:Floyd算法。

 

2. 问题分析

问题的提法:给定一个带权有向D与源点v,求从v到D中其它顶点的最短路径。限定各边上的权值大于0。

解决思路:Dijkstra提出按路径长度的递增次序,逐步产生最短路径的算法。首先求出长度最短的一条最短路径,再参照它求出长度次短的一条最短路径,以此类推,直到从顶点v到其它各顶点的最短路径全部求出为止。

 

3. 解决步骤描述

设置辅助数组dist。它的每一个分量dist[i]表示当前找到的从源点v0到终点vi最短路径的长度。

初始状态:若从源点v0到顶点vi有边:dist[i]为该边上的权值。

若从源点v0到顶点vi无边:dist[i]为无穷。

 

(1)      初始化:;  dist[j] Edge[0][j],j = 1,2,...,n-1;

找出最短路径所对应的点K:

(2)      dist[k] == min{dist[i]},;

;

(3)      对于每一个修改:

dist[i] min{dist[i],dist[k]+Edge[k][i]}

(4)      判断:若S=V,则算法结束,否则转(2)

 

4. 算法精髓

S集内的顶点是已经找到最短路径的顶点。

V0到w的最短路径只能通过S集内的顶点。

 

D[w]可能改变:

if(D[u] + edge[u, w] <D[w])

{

D[w] = D[u] +edge[u,w];

}

 

 

 

5. 举例:源点为0

 

 

6. 程序--最短路径算法的实现

Dijkstra.c

#include <stdio.h>

#include <stdlib.h>

 

/* run this program using the console pauser or add your own getch, system("pause") or input loop */

 

#define VNUM 5

#define MV 65536

 

int P[VNUM];

int Dist[VNUM];

int Mark[VNUM];

int Matrix[VNUM][VNUM] =

{

    {0, 10, MV, 30, 100},

    {MV, 0, 50, MV, MV},

    {MV, MV, 0, MV, 10},

    {MV, MV, 20, 0, 60},

    {MV, MV, MV, MV, 0},

};

 

void Dijkstra(int sv) // O(n*n)

{

    int i = 0;

    int j = 0;

   

    if( (0 <= sv) && (sv < VNUM) )

    {

        for(i=0; i<VNUM; i++)

        {

            Dist[i] = Matrix[sv][i];

            P[i] = sv;

            Mark[i] = 0;

        }

       

        Mark[sv] = 1;

       

        for(i=0; i<VNUM; i++)

        {

            int min = MV;

            int index = -1;

           

            for(j=0; j<VNUM; j++)

            {

                if( !Mark[j] && (Dist[j] < min) )

                {

                    min = Dist[j];

                    index = j;

                }

            }

           

            if( index > -1 )

            {

                Mark[index] = 1;

            }

           

            for(j=0; j<VNUM; j++)

            {

                if( !Mark[j] && (min + Matrix[index][j] < Dist[j]) )

                {

                    Dist[j] = min + Matrix[index][j];

                    P[j] = index;

                }

            }

        }

       

        for(i=0; i<VNUM; i++)

        {

            int p = i;

           

            printf("%d -> %d: %d\n", sv, p, Dist[p]);

           

            do

            {

                printf("%d <- ", p);

                p = P[p];

            } while( p != sv );

           

            printf("%d\n", p);

        }

    }

}

 

int main(int argc, char *argv[])

{

    Dijkstra(0);

 

         return 0;

}

7. 所有顶点之间的最短路径

问题的提法:

已知一个各边权值均大于0的带权有向图,对每一对顶点vi≠vj,要求求出vi 与vj之间的最短路径和最短路径长度。

方法:

Dijkstra:把有向图中的每一个顶点作为源点,重复执行。

 

8. Floyd算法基本思想

定义一个n阶方阵序列:A(-1), A(0), …, A(n-1)

其中:A(-1) [i][j] = Edge[i][j]

A(k) [i][j] = min { A(k-1)[i][j],A(k-1)[i][k] + A(k-1)[k][j] }

k = 0, 1, …, n-1

 

9. A矩阵的意义

A(0) [i][j]是从顶点vi到vj,中间顶点是v0的最短路径的长度。

A(k) [i][j]是从顶点vi到vj,中间顶点的序号不大于k的最短路径的长度。

A(n-1) [i][j]是从顶点vi到vj的最短路径长度。

 

 

 

 

 

10. Floyd算法的实现

Floyd.c    

#include <stdio.h>

#include <stdlib.h>

 

/* run this program using the console pauser or add your own getch, system("pause") or input loop */

 

#define VNUM 5

#define MV 65536

 

int P[VNUM][VNUM];

int A[VNUM][VNUM];

int Matrix[VNUM][VNUM] =

{

    {0, 10, MV, 30, 100},

    {MV, 0, 50, MV, MV},

    {MV, MV, 0, MV, 10},

    {MV, MV, 20, 0, 60},

    {MV, MV, MV, MV, 0},

};

 

void Floyd() // O(n*n*n)

{

    int i = 0;

    int j = 0;

    int k = 0;

   

    for(i=0; i<VNUM; i++)

    {

        for(j=0; j<VNUM; j++)

        {

            A[i][j] = Matrix[i][j];

            P[i][j] = j;

        }

    }

   

    for(i=0; i<VNUM; i++)

    {

        for(j=0; j<VNUM; j++)

        {

            for(k=0; k<VNUM; k++)

            {

                if( (A[j][i] + A[i][k]) < A[j][k] )

                {

                    A[j][k] = A[j][i] + A[i][k];

                    P[j][k] = P[j][i];

                }

            }

        }

    }

   

    for(i=0; i<VNUM; i++)

    {

        for(j=0; j<VNUM; j++)

        {

            int p = -1;

           

            printf("%d -> %d: %d\n", i, j, A[i][j]);

           

            printf("%d", i);

           

            p = i;

           

            do

            {

                p = P[p][j];

               

                printf(" -> %d", p);

            } while( p != j);

           

            printf("\n");

        }

    }

}

 

int main(int argc, char *argv[])

{

    Floyd();

   

         return 0;

}

 

小结:

(1)      Dijkstra最短路径算法是基于递推的思想设计的。

(2)      未达顶点的最短路径一定是由已达顶点的最短路径。

(3)      Floyd最短路径算法只是Dijkstra最短路径算法的加强,其本质还是递推。

posted @ 2019-08-11 18:50  free-锻炼身体  阅读(188)  评论(0编辑  收藏  举报