Dijkstra算法求解单源最短路径问题

一 前言

  上一次的文章更新还是在5月份,现在都快8月份了,整整三个月没有记录学习的点点滴滴。哎,主要是现在自己面临读研的一系列事情,到现在面试结果也没有出来,如果没中,那真的要给老师,给招生办跪下了,呜~~~~~~, 小菜鸡在大佬面前瑟瑟发抖,给我一个读书的机会吧!!!
   本次记录的是最短路径算法中的单源最短路经Dijkstra算法,也就是确定图中某一个点,求出图中其他所有点到该点的最短距离分别是多少

二 Dijkstra 算法讲解

1. 贪心算法的证明

Dijkstra算法的基本思想是贪心思想,关于贪心算法求解问题时,我们需要进行证明两点:

  1. 最优子结构
  2. 局部最优性(局部最优解可以得到全局最优解)
    关于算法的证明,小编就不多说了,这里直接分享一篇博客:迪杰斯特拉(Dijkstra)算法最通俗易懂的讲解 ,大家要是觉得解释的不是很好,最好还是去算法书上看看 。
2. 算法实现说明

在实现过程中,重要的就是我们引入了两个数组:dis数组和vis数组
dis数组:用来记录每个点到原点的最短路经。
vis数组:用来标记,某个点是否已经扩展过。

我们要做的就是在所有未扩展的点中,选出一个dis值最小的点进行松弛操作,这样n - 1次操作之后,即可知道原点到所有其他点的最短距离。
初始化操作
算法核心操作:
  现在假设我们已知结点A到源点S的最短距离为X,也存在从结点A指向结点B的有向边,该边的权值为val,显然结点B到原点会有一个距离,我们假设为Y,若X + val < Y,换句话说就是,我们现在找到了源点S到结点B的更短的一个路径,此时我们更新dis数组中到结点B到源点的距离值。

现在我们模拟一遍算法的执行流程

  1. 首先初始化dis数组和vis数组,dis数组中源点的值为0,剩余点的dis值为极大值;vis数组中均为0 ,其中0表示没有扩展,1表示该点已被扩展。
  2. 遍历dis数组,找到值最小,且没有扩展(vis值为0)的点,此时为1号结点。观察图可知,1号结点可以到达2, 3号结点,所以计算可知,结点2到源点的距离为6 + 0 = 6 < dis[2],结点3到源点的距离为0 + 1 = 1 < dis[3], 所以2, 3号结点均可以被更新。至此1号结点已经被扩展,标记vis[1] = 1;
    第一轮
  3. 遍历dis数组,找到值最小,且没有扩展(vis值为0)的点,此时为3号结点。观察图可知,3号结点可以到达2,4,6号结点,计算可知:2号结点到源点的距离为3 + dis[3] = 3 + 1 = 4(连接3号结点和2号结点的边的权值为3,3号结点到源点的最小距离为1)这个新的距离比原来2号结点到源点的距离6要小,所以我们需要进行距离的更新。同理,我们更新4和6号结点的距离。
    第二轮
  4. 按照之前的规则,我们相似操作,最终,我们可以得到如下的结果
    第五轮
3. 初版Dijkstra算法代码

我们以洛谷第3371题为例子:P3371 【模板】单源最短路径(弱化版)
解题代码如下:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <stack>
#include <algorithm>
#include <string>
#include <map>
#include <set>
#include <vector>
#include <cstring>
#include <cmath>
using namespace std;

#define MAX_N 10000
#define MAX_M 500000
#define INF 0x3f3f3f3f

struct edge {
   
   
    int to, val, next;
} edg[MAX_M + 5];

int head[MAX_N + 5], vis[MAX_N + 5], dis[MAX_N + 5];
int edg_cnt;

// 此处采用链式前向星存储有向图
void add_edge(int a, int b, int c) {
   
   
    edg[edg_cnt].to = b;
    edg[edg_cnt].val = c;
    edg[edg_cnt].next = head[a];
    head[a] = edg_cnt++;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值