算法学习记录图——最短路径之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)。(简化的粗估计)

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

原文地址:https://www.cnblogs.com/jsgnadsj/p/3425958.html