数据结构知识整理 - 应用图 - 最短路径(迪杰斯特拉算法和弗洛伊德算法)

版权声明: https://blog.csdn.net/Ha1f_Awake/article/details/85186490

主要内容


 

基本概念

最简单的最短路径是求中转次数最少的路径,而不考虑每条边的权值。而在实际问题中,路径长度的度量就不再是路径上的边数,而是路径上所有边的权值之和

有向网中,习惯上称路径的第一个顶点为源点(Source),最后一个顶点为终点(Destination)

下面主要讨论两种最常见的最短路径问题:

一、从某个源点到其余顶点的最短路径;

二、求每一对顶点之间的最短路径。

迪杰斯特拉(Dijkstra)算法用于求解第一种问题,时间复杂度为O(n²)弗洛伊德(Floyd)算法用于求解第二种问题,时间复杂度为O(n³)

从实现形式上来说,弗洛伊德算法比迪杰斯特拉算法更为简洁

迪杰斯特拉算法(单源点)

<逻辑思路>

(1)从单源点出发,求到各个顶点的最短路径。该问题类似于就有向连通网最小生成树

(2)因此同样需要辅助数组来记录顶点是否被归并最短路径长度

(3)迪杰斯特拉算法的巧妙之处就在于设计了三个辅助数组:

1. 一维数组S[]:记录顶点vi是否被确定最短路径长度,即该顶点是否被归并到终点集合中。初始化:S[v0-1] = TRUE;

2. 一维数组Path[]:记录顶点vi的直接前驱。譬如存在最短路径<v1,v4,v3>,v4的直接前驱是v1,v3的直接前驱是v4,这样一来,依靠直接前驱数组就能将路径连接起来。初始化:若源点v0到vi有弧,则Path[vi-1]为v0-1(编号/下标);否则为-1。

3. 一维数组D[]:记录从源点v0到终点vi的最短路径长度初始化:若源点v0到vi有弧,则D[vi-1]为弧上的权值;否则为∞。

(4)算法开始时,先找到D[]上的最小值,然后并入第一个终点v1,并将S[v1-1]的值设为TRUE;

(5)由于加入了终点v1,相当于v0多了一个中转点,所以需要对D[]上的值进行更新;

(6)更新后继续找D[]上的最小值,继而找到了顶点v2,因为S[v2-1]的值为FALSE,即v2未被归并到终点集合,所以可以将它并入;

(7)若到v2的最短路径是<v0,v1,v2>,则将v2的直接前驱改为v1,即Path[v2-1] = v1-1

(8)反复执行以上过程(n-1次),直至所有顶点被并入终点集合

<实现思路>——以邻接矩阵为存储结构的有向网

(1)对应<逻辑思路>步骤(3)的初始化:

1. S[v0-1] = TRUE;

2. D[vi-1] = G.arcs[v0-1][vi-1];

3. if(G.arcs[v0-1][vi-1] != MaxInt) Path[vi-1] = v0-1;              /*MaxInt表示无穷∞,值为326767*/

(2)对应<逻辑思路>步骤(8)的n-1次循环

1. 步骤(4):D[v1]  = Min(D);

2. 步骤(6):if(S[v1-1] = FALSE) S[v1-1] = TRUE;

3. 步骤(5)和步骤(7): if(D[v1-1] + G.arcs[v1-1][v2-1] < D[v2-1])  {D[v2-1] = D[v1-1] + G.arcs[v1-1][v2-1]; Path[v2-1] = v1-1;}

#define TRUE 1
#define FALSE 0

void ShortestPath_DIJ(AMGraph G, int v0)
{
    int S[vexnum];
    int Path[vexnum];
    int D[vexnum];

    int i = LocateVex(G, v0);

    for(int vi = 1; vi <= G.vexnum; vi++) /*初始化*/
    {
        int j  =LocateVex(G, vi);

        S[j] = FALSE;

        D[j] = G.arcs[i][j];

        if(D[j] < MaxInt) Path[j] = i;    /*小于MaxInt即v0与vi间存在弧*/
        else Path[j] = -1;                /*若vi的直接前驱不是v0,则置为-1*/
    }
    
    S[i] = TRUE;
    D[i] = 0;                             /*源点到源点的路径长度为0*/

/*----------初始化结束-----------*/

    for(int t = 1; t < G.vexnum; t++)     /*循环n-1次*/
    {
        int v;                            /*下一个终点*/

        for(vi = 1; vi <= G.vexnum; vi++)
        {
            int min = MaxInt;             /*min保存最小值*/

            int j = LocateVex(G, vi);

            if(S[j] = FALSE && D[j] < min)/*找出最小权值的边*/
                {v = vi; min = D[j];}
        }

        S[v-1] = TRUE;                    /*将v加入终点集合*/
        
        int k = LocateVex(G, v);
        
        for(vi = 1; vi <= G.vexnum; vi++) /*更新最短路径和直接前驱数组*/
        {
            int j = LocateVex(G, vi);

            if(S[j] = FALSE && (D[k] + G.arcs[k][j] < D[j])
            {
                D[j] = D[k] + G.arcs[k][j];
                Path[j] = k;
            }
        }
    }
}
                

弗洛伊德算法(顶点对)

求解每一对顶点之间的最短路径有两种方法:

一种是n次调用迪杰斯特拉算法;

另一种是采用下面介绍的弗洛伊德算法。

两种算法的时间复杂度均为O(n³),但弗洛伊德算法的形式更为简洁。

<逻辑思路>

(1)如果说迪杰斯特拉算法是一个“加边”的过程,那么弗洛伊德算法就是一个“加点”的过程;

(2)先确定两个目标顶点,源点为vs,终点为ve;

(3)若vs和ve之间存在弧,初始化为arcs[vs-1][ve-1](编号),反之置为∞;

(4)依次判断剩余顶点vi,若在两个顶点间插入vi后,使得<vs,vi>+<vi,ve>小于<vs,ve>,则置换为vs和ve间的最短路径;

(5)直至所有剩余顶点判断完毕,最短路径确定;

(6)其余的每一对顶点都重复上述过程。

<实现思路>——以邻接矩阵为存储结构的有向网

(1)该算法的核心思想是设置了两个二维辅助数组:

1. 二维数组Path[][]:行表示顶点vi的直接前驱,列表示顶点vi;

2. 二维数组D[]][]:记录vs和ve间的最短路径。

(2)弗洛伊德算法的代码思路比较清晰,插入、比较、更新。

void ShortestPath_Floyd(AMGraph G)
{
/*-----------初始化------------*/
    for(int vs = 1; vs <= G.vexnum; vs++)         /*源点vs*/
    {
        int i = LocateVex(G, vs);

        for(int ve = 1; ve <= G.vexnum; ve++)     /*终点ve*/
        {
            int j = LocateVex(G, ve);

            D[i][j] = G.arcs[i][j];
            
            if(D[i][j] < MaxInt && i != j)
                Path[i][j] = i;
            else Path[i][j] = -1;
        }
           
/*---------初始化结束--------*/

    for(int vi = 1; vi <= G.vexnum; vi++)         /*插入点vi*/
        for(int vs = 1; vs <= G.vexnum; vs++)     /*源点vs*/
            for(int ve = 1; ve <= G.vexnum; ve++) /*终点ve*/
                {
                    int k = LocateVex(G, vi);
                    int i = LocateVex(G, vs);
                    int j = LocateVex(G, ve);
            
                    if(D[i][k] + D[k][j] < D[i][j])       /*更新*/
                    {
                        D[i][j] = D[i][k] + D[k][j];
                        
                        Path[i][j] = k;
                    }
                }
}

猜你喜欢

转载自blog.csdn.net/Ha1f_Awake/article/details/85186490