【算法】Dijkstra算法(一)用邻接矩阵表示的概念理解

目录

前言:

迪杰斯特拉算法的定义:

用邻接矩阵的方法来理解:

初始化:

定义中的操作:

这里有问题

分析

代码


前言:

这个看了很多很多天了,每次看得昏昏欲睡脑袋晕晕乎乎就是看不明白。有些怀疑自己脑袋瓜是不是秀逗了。o(╥﹏╥)o,这是第六天了,我还是没看明白,本来打算看懂弄透了再写个博客巩固下,结果这个博客八成又变成了问题记录贴。

好吧,开始吧。

迪杰斯特拉算法的定义:

Dijkstra算法用来解决单源最短路问题,即给定图G和起点s,通过算法得到S到达其他每个顶点的最短距离。Dijkstra的基本思想是对图G(V,E)设置集合S,存放已被访问的顶点,然后每次从集合V-S中选择与起点s的最短距离最小的一个顶点(记为u),访问并加入集合S。之后,令顶点u为中介点,优化起点s与所有从u能到达的顶点v之间的最短距离。这样的操作执行n次(n为顶点个数),直到集合S已包含所有顶点。

上面这一堆话表示我第一次看的时候压根没看明白,图什么的全忘光了。

去啃了下算法笔记,大概意思是:

用邻接矩阵的方法来理解:

初始化:

你需要定义一个二维数组G[MAX_V][MAX_V]表示一个图数组的值是边权(路径长度)。

此外设置一个bool型的数组Vis[MAX_V]来表示定义中集合S的一个访问属性,即这个bool型的数组Vis表示该点有没有被访问过,因为这个集合S抽象化后这样表示比较符合。

接着定义一个数组d[MAX_V]表示最短距离,即存储到对应下标最短距离的数组。对应定义中的集合V-S。

定义中的操作:

①每次从集合V-S中选择与起点s的最短距离最小的一个顶点(记为u),访问并加入集合S:

即d[MAX_V]数组中存放的是下标对应的顶点距起始点s的最短距离,这个集合开始被初始化为全0,数组d[s]是不断扩大的,即每次找到一个距离起点s距离最近的的一个点,找到了之后,将这个点的信息保存到数组d[MAX_V]中。 

那么访问并加入集合S:

加入集合S这里是加入到了数组d中,例如

第一轮

距离s最近的一个点是他自己s,u=s;访问并加入集合即保存d[s]=0;(d[u]=0)

有没有访问则是上面的Vis[MAX_V]数组来记录。即这个顶点u找到后记录该u点被访问过了:Vis[u]=true;

②之后,令顶点u为中介点,优化起点s与所有从u能到达的顶点v之间的最短距离。

 因为d[]数组是不断更新的,并不是一开始就定义好的,因为他表示的是到对应下标点的最短距离,这个距离是需要自己计算的。

举例子来说,例如u的下一个顶点v距离他最近,且距离是5。

优化:

这个时候需要比较一下:s到u再到v的距离 是否比s直接到v的距离短(因为不知道s到v之间是不是有一条路),如果是d[u]+G[u][v]更短的,那么则更新d[v]=d[u]+G[u][v]。否则还是保存原来的信息。???

这里有问题

这里的优化我一直没有理解清楚:

这里的‘优化’利用的是一个for循环,遍历对比中介点到其他所有顶点,如果发现以自己为中介点的路径比d[]中存储过的小那么更新,如果大就不更新,如果一样,可以定义一个新的变量存储为多条路径。

值得注意的是,中介点并不是只是找到一个距离自己最近的更新,而是和每一个点都进行对比存储,比较的是当前中介点的最短距离和以前更新的到该点的最短距离。

又有问题:比较的是当前中介点的最短距离和以前更新的到该点的最短距离。

这里通过后面的代码可以看到,他是对每一个顶点都进行排查对比没错但是在循环中,if的条件指明:不可以是访问过的顶点,只能是未被访问过的顶点才可以更新

自己觉得:可能访问过的也可以比较一下,但是没必要,像下图到V2了,以V2为中介点,这个时候之前的都已经‘尘埃落定’,该闹的在前面岔路口V1已经闹过了,所以只要对未被访问的进行比较就可以了。

分析

说的可能很抽象,自己画个图简单分析下:

③这样的操作执行n次(n为顶点个数),直到集合S已包含所有顶点。

执行n此即上面图中的步骤:

从d[]中找到值最小的一个u,找到后以这个点为中介点,然后更新他所能更新的点的距离(不一定只更新一个,可能可以更新多个,如上图中第二轮),这里的更新是需要比较的。

如此往复,直到所有的顶点都在d[]中,即执行n次,那么d[dest],目标终点的d[]即是起始点到达终点最短路径。

代码

下面结合代码具体实现理解一下:

#include <iostream>
using namespace std;
int INF=1e9;
int MAXV=500;
int main()
{
    //初始化部分
    int N,M,C1,C2,length,num[MAXV];
    int G[MAXV][MAXV],weight[MAXV],W[MAXV],d[MAXV];
    bool vis[MAXV];
    for(int i=0; i<MAXV; i++)
    {
        d[i]=INF;
        W[i]=0;
        vis[i]=false;
        for(int j=0; j<MAXV; j++)
            G[i][j]=INF;
    }
    cin>>N>>M>>C1>>C2;
    int st=C1,ed=C2;
    for(int i=0; i<N; i++)
    {
        cin>>weight[i];
    }
    for(int i=0; i<M; i++)
    {
        cin>>C1>>C2>>length;
        G[C1][C2]=length;
    }

    // 迪杰斯特拉算法
    d[st]=0;
    W[st]=weight[st];
    num[st]=1;

    for(int i=0; i<N; i++)
    {
        //每次都从数组d[]中找到一个距离最小的点且未被访问的点作为中介点,找到后并标记访问了
        int min=INF,u=-1;
        for(int j=0; j<N; j++)
        {
            if(vis[j]==false&&d[j]<min)
            {
                min=d[j];
                u=j;
            }
        }
        if(u==-1)return 0;
        vis[u]=true;
        //已u作为中介点,对每一个顶点进行查询,看看可不可以以u作为桥梁,成功更新d[v]
        for(int v=0; v<N; v++)
        {
            //如果顶点没有被访问过而且之间有路,那么就可以进行更新了。
                if(vis[v]==false&&G[u][v]!=INF)
                {
                    //如果距离小就更新
                    if(d[u]+G[u][v]<d[v])
                    {
                        d[v]=d[u]+G[u][v];
                        W[v]=W[u]+weight[v];
                        num[v]=num[u];
                    }
                    //碰到一样的

                    else if(d[u]+G[u][v]==d[v])
                    {
                        if(W[u]+weight[v]>W[v])
                        {
                            W[v]=W[u]+weight[v];
                        }
                        num[v]+=num[u];
                    }
                }
        }
    }
    cout<<num[ed]<<" "<<W[ed];
    return 0;
}

运行结果:(这个运行结果目前还有问题,暂时还没找出来上面代码哪里写的有问题,不过算法的步骤应该是和上面的意思是一样的,不过多了点权的概念,其实也就是在更新的时候多更新了一下点权的和而已,本质上并没有什么变化。) 

 enmmm,目前先到这里,感觉图这一部分好多好多的知识点,而且还有邻接表的方法我还没有看,更别提那些深度优先遍历什么鬼的了。

oh my godo(╥﹏╥)o,我这可怜的小脑瓜,什么时候可以再灵光点。

看到这个博客:

https://blog.csdn.net/qq_33579045/article/details/80008332

里面的举例非常容易理解,可以一起看下。

发布了68 篇原创文章 · 获赞 20 · 访问量 6891

猜你喜欢

转载自blog.csdn.net/qq_43633973/article/details/102226913
今日推荐