• 算法学习记录图——最短路径之Dijkstra算法


    在网图中,最短路径的概论:

    两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点是源点,最后一个顶点是终点。

    维基百科上面的解释:

    这个算法是通过为每个顶点 v 保留目前为止所找到的从s到v的最短路径来工作的。

    初始时,原点 s 的路径长度值被赋为 0 (d[s] = 0),若存在能直接到达的边(s,m),则把d[m]设为w(s,m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大,即表示我们不知道任何通向这些顶点的路径(对于 V 中所有顶点 v s 和上述 m  d[v] = ∞)。

    当算法退出时,d[v] 中存储的便是从 s  v 的最短路径,或者如果路径不存在的话是无穷大。

     

    Dijkstra 算法的基础操作是边的拓展:如果存在一条从 u  v 的边,那么从 s  v 的最短路径可以通过将边(u, v)添加到尾部来拓展一条从 s 到 u 的路径。

    这条路径的长度是 d[u] + w(u, v)。如果这个值比目前已知的 d[v] 的值要小,我们可以用新值来替代当前 d[v] 中的值。

    拓展边的操作一直运行到所有的 d[v] 都代表从 s 到 v 最短路径的花费。

    这个算法经过组织因而当 d[u] 达到它最终的值的时候每条边(u, v)都只被拓展一次。

     

    算法维护两个顶点集 S 和 Q。集合 S 保留了我们已知的所有 d[v] 的值已经是最短路径的值顶点,而集合 Q 则保留其他所有顶点。

    集合S初始状态为空,而后每一步都有一个顶点从 Q 移动到 S。

    这个被选择的顶点是 Q 中拥有最小的 d[u] 值的顶点。当一个顶点 u 从 Q 中转移到了 S 中,算法对每条外接边 (u, v) 进行拓展。

    后来我根据自己的理解,该算法基本可以分为这样几个步骤:

    我用网上的一个图为例,说明该算法的执行过程。

    下面来看代码:

    void Dijkstra(MGraph g,int Vs,int *path,int *Dest)
    {
        int i,j,k;
        EdgeType min;
        int isshort[MAXVEX];
        
        //初始化path前驱,Vs起始点到其他点的距离。
        for (i =0;i<g.numVexs;i++)
        {
            isshort[i]=0;
            path[i]=0;
            Dest[i]=g.Mat[Vs][i];
        }
    
        //Vs选入isshort中
        isshort[Vs]=1;
        //Vs到Vs的距离为0
        Dest[Vs]=0;
    
        for (i=1;i<g.numVexs;i++)
        {
            //寻找当前最小的路径
            min = IFY;
            for (j=0;j<g.numVexs;j++)
            {
                if (isshort[j] == 0 && Dest[j] < min)
                {
                    min = Dest[j];
                    k=j;
                }
            }
            //把最小的标号记录,且该下标的顶点进入isshort
            isshort[k]=1;
    
            //在k为下标的顶点时候,寻找以k为起点,和k相连且没有归入到isshort中的点,
            //Dest中存放的是 各个顶点到Vs点的最短距离。
            //此时,如果通过K点到达各个点的距离还要小,则更新Dest数组。
            for (j=0;j<g.numVexs;j++)
            {
    
                if (isshort[j] == 0 && ((min + g.Mat[k][j])  < Dest[j]) )
                {
                    Dest[j]=min+g.Mat[k][j];
                    path[j]=k;
                }
            }
        }
    }

    代码中的第一个for循环再第一幅图中表示。第二个for循环,依次为标号为1,。。。9的图。可以对着看。

    完整代码:

    // grp-dijkstra.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include <stdlib.h>
    #include <string.h>
    
    
    #define MAXVEX 100
    #define IFY 65535
    
    typedef char VertexType;
    typedef int  EdgeType;
    
    
    bool g_visited[MAXVEX];
    
    VertexType g_init_vexs[MAXVEX] = {'A','B','C','D','E','F','G','H','I'};
    
    EdgeType g_init_edges[MAXVEX][MAXVEX] = {
        {0,2,5,IFY,IFY,IFY,IFY,IFY,IFY},    //'A'
        {2,0,2,8,5,IFY,IFY,IFY,IFY},  //'B'
        {5,2,0,IFY,1,6,IFY,IFY,IFY},//'C'
        {IFY,8,IFY,0,3,IFY,2,IFY,IFY},//'D'
        {IFY,5,1,3,0,5,8,7,IFY},    //'E'
        {IFY,IFY,6,IFY,5,0,IFY,4,IFY},    //'F'
        {IFY,IFY,IFY,2,8,IFY,0,2,9},    //'G'
        {IFY,IFY,IFY,IFY,7,4,2,0,3},    //'H'
        {IFY,IFY,IFY,IFY,IFY,IFY,9,3,0},    //'I'
    };
    
    
    //静态图-邻接矩阵
    typedef struct {
        VertexType vexs[MAXVEX];
        EdgeType Mat[MAXVEX][MAXVEX];
        int numVexs,numEdges;
    }MGraph;
    
    
    //====================================================================
    //打印矩阵
    void prt_maxtix(EdgeType *p,int vexs)
    {
        int i,j;
        for (i=0;i<vexs;i++)
        {
            printf("\t");
            for (j=0;j<vexs;j++)
            {
                if( (*(p + MAXVEX*i + j)) == IFY)
                {
                    printf("  $ ");
                }
                else
                {
                    printf(" %2d ", *(p + MAXVEX*i + j));
                }
            }
            printf("\n");
        }
    }
    
    //check the number of vextex
    int getVexNum(VertexType *vexs)
    {
        VertexType *pos = vexs;
        int cnt=0;
        while(*pos <= 'Z' && *pos >= 'A')
        {
            cnt++;
            pos++;
        }
        return cnt;
    }
    
    bool checkMat(EdgeType *p,VertexType numvex)
    {
        int i,j;
        for (i=0;i<numvex;i++)
        {
            for(j=i+1;j<numvex;j++)
            {
                //printf("[%d][%d] = %d\t",i,j,*(p + MAXVEX*i + j));
                //printf("[%d][%d] = %d\n",j,i,*(p + MAXVEX*j + i));
                if (*(p + MAXVEX*i + j) != *(p + MAXVEX*j +i) )
                {
                    printf("ERROR:Mat[%d][%d] or Mat[%d][%d] not equal!\n",i,j,j,i);
                    return false;
                }
            }
        }
        return true;
    }
    
    void init_Grp(MGraph *g,VertexType *v,EdgeType *p)
    {
        int i,j;
        // init vex num
        (*g).numVexs = getVexNum(v);
    
        //init vexter 
        for (i=0;i<(*g).numVexs;i++)
        {
            (*g).vexs[i]=*v;
            v++;
        }
    
        //init Mat 
        for (i=0;i<(*g).numVexs;i++)
        {
            for (j=0;j<(*g).numVexs;j++)
            {
                (*g).Mat[i][j] = *(p + MAXVEX*i + j);
            }
        }
        if(checkMat(&((*g).Mat[0][0]),(*g).numVexs) == false)
        {
            printf("init error!\n");
            getchar();
            exit(0);
        }
    }
    
    void Dijkstra(MGraph g,int Vs,int *path,int *Dest)
    {
        int i,j,k;
        EdgeType min;
        int isshort[MAXVEX];
        
        //初始化path前驱,Vs起始点到其他点的距离。
        for (i =0;i<g.numVexs;i++)
        {
            isshort[i]=0;
            path[i]=0;
            Dest[i]=g.Mat[Vs][i];
        }
    
        //Vs选入isshort中
        isshort[Vs]=1;
        //Vs到Vs的距离为0
        Dest[Vs]=0;
    
        for (i=1;i<g.numVexs;i++)
        {
            //寻找当前最小的路径
            min = IFY;
            for (j=0;j<g.numVexs;j++)
            {
                if (isshort[j] == 0 && Dest[j] < min)
                {
                    min = Dest[j];
                    k=j;
                }
            }
            //把最小的标号记录,且该下标的顶点进入isshort
            isshort[k]=1;
    
            //在k为下标的顶点时候,寻找以k为起点,和k相连且没有归入到isshort中的点,
            //Dest中存放的是 各个顶点到Vs点的最短距离。
            //此时,如果通过K点到达各个点的距离还要小,则更新Dest数组。
            for (j=0;j<g.numVexs;j++)
            {
    
                if (isshort[j] == 0 && ((min + g.Mat[k][j])  < Dest[j]) )
                {
                    Dest[j]=min+g.Mat[k][j];
                    path[j]=k;
                }
            }
        }
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        MGraph grp;
        EdgeType Dest[MAXVEX];
        EdgeType Path[MAXVEX];
        
        EdgeType edgeDest[MAXVEX][MAXVEX];
        EdgeType edgePath[MAXVEX][MAXVEX];
    
        int i;
    
        init_Grp(&grp,g_init_vexs,&g_init_edges[0][0]);
        //prt_maxtix(&grp.Mat[0][0],grp.numVexs);
        printf("Dijkstr start!\n");
        for (i=0;i<grp.numVexs;i++)
        {
            Dijkstra(grp,i,Path,Dest);
            memcpy(edgeDest[i],Dest,grp.numVexs*sizeof(Dest[0]));
            memcpy(edgePath[i],Path,grp.numVexs*sizeof(Path[0]));
    
            memset(Dest,0,grp.numVexs*sizeof(Dest[0]));
            memset(Path,0,grp.numVexs*sizeof(Path[0]));
        }
        //Dijkstra(grp,0,Path,Dest);
        printf("Dest vs to ve :\n");
        prt_maxtix(&edgeDest[0][0],grp.numVexs);
    
        printf("Path matix :\n");
        prt_maxtix(&edgePath[0][0],grp.numVexs);
        
        printf("finish\n");
        getchar();
        return 0;
    }

    结果:

    这个结果的意思是:

    Dest vs to ve:

    每一行为一个单独,比如第一行,表示V0到Vx的距离。与我们之前分析的第10幅图一样。

    从这个表里面可以看出,任何一个点到另一个点的最短距离。

    Path Matix:

    最短路径所经过的点,对照第10幅图来看,就知道怎么线索路径,反一下就知道路径点。

    这个可以看出,在主程序里面一个for

    在Dijkstra中,for 中嵌套for,时间复杂度为O(N3)。(简化的粗估计)

    对于大量的点,该方法效率会非常的低。因为对于每一个点,它都将遍历图中的其他每一个点。

  • 相关阅读:
    疫情控制
    数据结构1
    NOIP 模拟 921
    ml-agents项目实践(一)
    Appium的安装及简单的使用介绍
    移动设备管理平台的搭建(基于STF/ATXServer2)
    ClickHouse利器—如何提高留存计算速度
    Linux基本操作命令
    深度学习与强化学习的两大联姻:DQN与DDPG的对比分析
    漏洞扫描软件AWVS的介绍和使用
  • 原文地址:https://www.cnblogs.com/jsgnadsj/p/3425958.html
Copyright © 2020-2023  润新知