Dijkstra算法理解

Dijkstra算法

如有错误请指正!谢谢!

这个算法最通俗的解释就是单源最短路径算法,展开来讲,就是从图中一个点到其他点的最短路径(好像是废话

Dijkstra算法的核心就是贪心,首先把各个点与起点的距离记录在一个dis数组(起初除了起点,其他的点的最段距离都是不确定的,所以需要一个vis数组来判断这个点是否是距离起点的最短路径点)中,从dis数组中找到最短的那个点,且这个点肯定是目前距离起点最近的(这个想不明白的看看图就知道了),然后以这个点为”中转站“,判断比较从中转站到别的点是否距离更短,如果更短,那就更新dis数组,如此更新(点数-1)次,就能保证所有的点到起点的距离最短。

还是不懂的话,上面几段话多看两遍,然后再结合下面的实例进行相应的理解。

--------------------------------------------------分割线--------------------------------------------------

就以下面的样例作为简单的解释吧

用的是邻接矩阵。

在这里插入图片描述

假设一开始起点为1。

用一个map数组存储点与点之间的边的权重,如图所示(手写的有点丑):

在这里插入图片描述

那么现在dis数组为0,1,3,∞,∞,∞,vis数组为1,0,0,0,0,0(起点标记为1)

现在距离起点1最近的点就是点2,所以点2就是一个确定的离起点最近的一个点,然后点2就作为中转站,此时还需要更新vis数组(1,1,0,0,0,0)用i从点1开始遍历到点6,比较通过中转站(点2)到达点i的距离是否小于dis中的值,如果小就更新,我们可以看到,从点2可以到点1,点4,点5(因为是无向图所以会有重复,但不影响判断),依次进行比较。

从点1到中转站(点2)到点4,那么权重和就是1+1=2,小于之前的∞,更新dis数组,0,1,3,2,∞,∞

从点1到中转站(点2)到点5,那么权重和就是1+5=6,小与之前的∞,更新dis数组,0,1,3,2,6,∞

点2作为中转站当前能得到的各个点的最短路径已经更新完毕,接下来就是从没有确定最短路径的点(即vis[i]=0)当中取最小的那个点作为中转站(即点4作为中转站),依此类推。

(有个专业名词叫”松弛“,其实就是上面找点作中转站,比较谁更短,然后更新的操作。)

样例输入:

6 8
1 2 1
2 5 5
5 6 2
6 4 9
4 3 7
3 1 3
3 5 10
2 4 1
1

#include<iostream>
#include<string.h>
using namespace std;
#define inf 0x3f3f3f3f
int map[1010][1010];//这是一个二维模拟数组,map[i][j]对应从i到j的边权(注意不要和边长混淆概念)
int vis[1010],dis[1010];//vis[i]代表i到某一点是否是确定的最短路径,dis[i]是确定的最短路径的值
int n,m;//n代表有多少点,m代表有多少边
int start;//代表起点
//本模板点下标从1到n
int main(){
    cin>>n>>m;
    //初始化很重要,不要忘记
    memset(map,inf,sizeof(map));
    for(int i=1;i<=n;i++){
        map[i][i]=0;
    }
    //读入图
    for(int i=0;i<m;i++){
        int u,v,w;
        cin>>u>>v>>w;
        map[u][v]=w;
        map[v][u]=w;
    }
    /*以下为Dijkstra实现(可抽取为一个函数)*/
    //vis数组一开始就是定义在main函数外,所以不需要memset,本身就全是0
    //接下来先初始化dis数组
    cin>>start;
    for(int i=1;i<=n;i++){
        dis[i]=map[start][i];
    }
    vis[start]=1;//因为是初始点,所以直接标记为访问过
    for(int i=1;i<n;i++){//为了确保每个点到起始点都是最短路径,就要进行n-1次的迭代,使得vis数组从1到n都是1,即都访问过
        //下面的操作是找出未确定最短路径的点中离初始点最近的一个点
        int min=inf,index;
        for(int j=1;j<=n;j++){
            if(!vis[j]&&dis[j]<min){
                min=dis[j];
                index=j;
            }
        }
        vis[index]=1;//表明已经确定最短路径
        //下面的操作就是逐个更新最段路
        for(int j=1;j<=n;j++){
            if(map[index][j]+dis[index]<dis[j]){
                dis[j]=map[index][j]+dis[index];
            }
        }
    }
    /*至此,Dijkstra实现结束*/
    //这边就进行一下简单的输出
    for(int i=1;i<=n;i++){
        cout<<dis[i]<<" ";
    }
    return 0;
}

注释写的好像太多了

样例输出:

0 1 3 2 6 8

标准模板

#include<iostream>
#include<string.h>
#define inf 0x3f3f3f3f
#define maxn 1010
using namespace std;
int e[maxn][maxn],vis[maxn],dis[maxn];
int n,m,start,dest;
int main(){
    //输入,略
    memset(e,inf,sizeof(e));
    memset(dis,inf,sizeof(dis));
    dis[start]=0;
    for(int i=0;i<n;i++){
        int u=-1,min=inf;
        for(int j=0;j<n;j++){
            if(!vis[j]&&dis[j]<min){
                u=j;
                min=dis[j];
            }
        }
        if(u==-1){
            break;
        }
        vis[u]=1;
        for(int v=0;v<n;v++){
            if(!vis[v]&&e[u][v]!=inf&&dis[u]+e[u][v]<dis[v]){
                dis[v]=dis[u]+e[u][v];
            }
        }
    }
    return 0;
}

无向图可以,有向图只要保证能到达,也是一样的,区别就在于邻接矩阵的设置。

好了,既然懂了,那不妨写道题目练练手(1003 Emergency

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值