Dijkstra算法简介

Dijkstra算法是由荷兰计算机科学家Edsger Wybe Dijkstra于1959年提出的一种解决有向加权图中单源最短路问题的算法,其中要求加权图中不可有负权边

Dijkstra算法步骤演示

  • 以如下的一张有向正权图G为例,规定:

    • 起点为A
    • 向量xy→表示从顶点x到顶点y的有向边\vec{xy}表示从顶点x到顶点y的有向边
    • 向量的模∣∣xy→∣∣表示有向边xy→的权值向量的模\left|\vec{xy}\right|表示有向边\vec{xy}的权值

  • 初始状态:
      设起点到各点当前最短距离为Lk(k=A,B,C,D,E),则有:L_k (k=A,B,C,D,E),则有:

    LA=0

    L_A=0
    并设此时

    LB至LE=+∞

    L_B至L_E=+\infty
    则可列表:

  LAL_A LBL_B LCL_C LDL_D LEL_E
初始状态 0 +∞+\infty +∞+\infty +∞+\infty +∞+\infty
  • 第一次迭代:
      从起点A点出发,更新起点到A的邻居(B、C、D)的当前最短距离(LkL_k)。此时:
    对B:

    LB=∣∣∣AB→∣∣∣=10

    L_B=\left|\vec{AB}\right|=10 对C:

    LC=∣∣∣AC→∣∣∣=3

    L_C=\left|\vec{AC}\right|=3 对D:

    LD=∣∣∣AD→∣∣∣=20

    L_D=\left|\vec{AD}\right|=20
    则可列表:

  LAL_A LBL_B LCL_C LDL_D LEL_E
第一次迭代后 0 10 3 20 +∞+\infty
  • 第二次迭代:
      找出第一次迭代后除已处理过的起点A外,LkL_k最小的点:C
      从C出发,更新C邻居(B、E)的LkL_k值,此时:
    对B:
      经过C到达B的路径长度:

    L=LC+∣∣∣CB→∣∣∣=5

    L=L_C+\left|\vec{CB}\right|=5

    ∵LB=10>L

    \because L_B=10>L

    ∴更新LB=L=LC+∣∣∣CB→∣∣∣=5

    \therefore 更新L_B=L=L_C+\left|\vec{CB}\right|=5
    对E:
      经过C到达E的路径长度:

    L=LC+∣∣∣CE→∣∣∣=18

    L=L_C+\left|\vec{CE}\right|=18

    ∵LE=+∞>L

    \because L_E=+\infty>L

    ∴更新LE=L=LC+∣∣∣CE→∣∣∣=18

    \therefore 更新L_E=L=L_C+\left|\vec{CE}\right|=18
    则可列表:

  LAL_A LBL_B LCL_C LDL_D LEL_E
第二次迭代后 0 5 3 20 18
  • 第三次迭代:
      找出第二次迭代后,除已处理过的A、C两点外,LkL_k最小的点:B
      从B出发,更新B邻居(D)的LkL_k值,此时:
    对D:
      经过B到达D的路径长度:

    L=LB+∣∣∣BD→∣∣∣=10

    L=L_B+\left|\vec{BD}\right|=10

    ∵LD=20>L

    \because L_D=20>L

    ∴更新LD=L=LB+∣∣∣BD→∣∣∣=10

    \therefore 更新L_D=L=L_B+\left|\vec{BD}\right|=10
    则可列表:

  LAL_A LBL_B LCL_C LDL_D LEL_E
第三次迭代后 0 5 3 10 18
  • 第四次迭代:
      找出第三次迭代后,除已处理过的A、B、C三点外,LkL_k最小的点:D
      从D出发,更新D邻居(E)的LkL_k值,此时:
    对E:
      经过D到E的路径长度:

    L=LD+∣∣∣DE→∣∣∣=21

    L=L_D+\left|\vec{DE}\right|=21

    ∵LE=18<L

    \because L_E=18

    ∴不必更新,LE=18

    \therefore 不必更新,L_E=18
    则可列表:

  LAL_A LBL_B LCL_C LDL_D LEL_E
第四次迭代后 0 5 3 10 18
  • 第五次迭代:
      找出第四次迭代后,除已处理过的A、B、C、D四点外,LkL_k最小的点:E
      此时,E没有邻居,因此对E的处理直接结束
      
    则可列表:
  LAL_A LBL_B LCL_C LDL_D LEL_E
第五次迭代后 0 5 3 10 18
  • 五次迭代后,有向图G中所有的点都被处理过,算法终止,则整个迭代过程列表如下:
  LAL_A LBL_B LCL_C LDL_D LEL_E
初始状态 0 +∞+\infty +∞+\infty +∞+\infty +∞+\infty
第一次迭代后 0 10 3 20 +∞+\infty
第二次迭代后 0 5 3 20 18
第三次迭代后 0 5 3 10 18
第四次迭代后 0 5 3 10 18
第五次迭代后 0 5 3 10 18

  不难观察到,Dijkstra算法的特点主要是从起点开始,“由近及远,层层扩展”,越靠前处理的点离起点越近,最后一个处理的点一定离起点最远。
  所以,依据算法每找到一个顶点后,该顶点对应的LkL_k值就是起点到该点的最短路径长度,且LkL_k在这之后不会被更改
  而最后一次迭代得到的所有LkL_k的值,就是由起点(亦称源点)A到各点的最短路径长度。
  故Dijkstra算法解决的是有向图中的单源最短路问题

算法概括:

  1. 步骤概括:

    • 第一个核心步骤:找到当前未处理过的顶点中LkL_k最小的点VV,(由于起点到起点的消耗为0,所以算法开始时V必定代表起点);
    • 第二个核心步骤:若V有邻居,则计算经过V的情况下起点到达各邻居的消耗LL,并选择是否更新VV邻居的LkL_k值。若没有邻居则对该点的处理结束
    • 重复以上两个核心步骤,直到满足算法终止的条件:有向图中所有的点都被处理过
  2. 流程图:

Created with Raphaël 2.1.0开始找到符合条件的V更新V的所有邻居图中所有顶点都被找到过?结束yesno

数学描述及证明

  • Dijkstra算法的数学描述:
    设全集UU:有向图中所有的点的集合。
    设点集SS :已经找到最短路径的点的集合,初始状态下设仅有 起点∈S起点\in S。
    设点集QQ:还未找到最短路径的点的集合,显然Q=U−SQ=U-S。
    设LkL_k为当前情况下,起点经过SS中若干点到点kk的最短距离(k∈Uk\in U),初始L起点=0L_{起点}=0,其他均为+∞+\infty。
    算法开始:

    • 从起点开始,沿某条弧(设权值为arcsarcs)找到起点的一个邻居nn
    • 令Ln=min{Ln,L起点+arcs}L_n=min\{L_n,L_{起点}+arcs\}
    • 按此方式更新起点所有邻居
    • 在集合QQ中找到LkL_k最小的点vv,则LvL_v即起点到vv的最短路径长度
    • 将点vv从QQ中取出加入SS,对点vv重复上述所有操作
    • 如此重复,直到S=US=U,即Q=∅Q=\emptyset时,算法结束,LkL_k即为从起点到各点的最短路径长度

提醒:这里读者一定要反复仔细体会LkL_k的含义,它不断更新的过程正是Dijkstra算法“由近及远,层层扩展”特点的体现。同时思考一下之前提过的“找到一个点后,该点LkL_k值肯定不会被更改”的原因(理解LkL_k的含义后,原因其实是显而易见的)。

  • Dijkstra算法的数学证明:
    由算法的数学描述,可知:
    只有命题:“每次从QQ集中找到LkL_k最小的点vv,LvL_v即为从起点到vv的最短路径长度”正确时,算法正确。
    可用广义数学归纳法证明,设起点为oo:

    • 证明:算法找到的第一个点为v1v_1,Lv1L_{v_1}即为从起点到v1v_1的最短路径长度。
      用反证法:用反证法:
      ∵算法找到的第一个点一定是起点o最近的邻居\because 算法找到的第一个点一定是起点o最近的邻居
      假设Lv1不是从起点到v1的最短路径长度假设L_{v_1}不是从起点到v_1的最短路径长度
      则∃点v,使得∣∣ov→∣∣<∣∣ov1→∣∣,与已知矛盾则\exists点v,使得\left|\vec{ov}\right|
      故假设不成立,子命题得证故假设不成立,子命题得证
    • 证明:已用算法从QQ中依次找到了v1,v2,⋯,vkv_1,v_2,\cdots,v_k共kk个点,且Lv1,Lv2,⋯,LkL_{v_1},L_{v_2},\cdots,L_k是起点到各点的最短路径长度,则此时从QQ中依照算法再找一个点vk+1v_{k+1},Lk+1L_{k+1}即为起点到vk+1v_{k+1}的最短路径长度。
      用反证法:用反证法:
      假设Lk+1不是起点到vk+1最短路径的长度假设L_{k+1}不是起点到v_{k+1}最短路径的长度
      所以设起点到vk+1的最短路径经过的点的集合为V,路径长度为L,则有L<Lk+1所以设起点到v_{k+1}的最短路径经过的点的集合为V,路径长度为L,则有L
      ∵由Lk的含义⇒V∩Q≠∅\because 由L_k的含义\Rightarrow V\cap Q\neq\emptyset
      又∵o∈V且o∈S⇒V∩S≠∅又\because o\in V且o\in S \Rightarrow V \cap S \neq\emptyset
      则设到vk+1的最短路径中,最靠近vk+1且不属于S的点为vx,vx的后继为vy则设到v_{k+1}的最短路径中,最靠近v_{k+1}且不属于S的点为v_x,v_x的后继为v_y
      ∵有向图中边均为正权边\because 有向图中边均为正权边
      ∴必有Lvx<Lvy⩽L,vy为vk+1时等号成立\therefore 必有L_{v_x}
      又∵vx∉S⇒Lvx>Lk+1,产生矛盾又\because v_x\not\in S\Rightarrow L_{v_x}>L_{k+1},产生矛盾
      故假设不成立,子命题得证故假设不成立,子命题得证

    综上所述,该命题得证,故算法正确。

关于负权边

Dijkstra算法要求有向图中不得有负权边,如果图中有负权边,则在之前的证明过程中:

∵Lvx<Lvy不一定成立(Lvy⩽L依然成立)

\because L_{v_x}

∴Lvx>Lk+1不一定成立

\therefore L_{v_x}>L_{k+1}不一定成立
故此时算法正确性不得证。

这里举一个简单的例子供读者自行对照理解:

时间复杂度

设有向图中,共有VV个顶点,EE条边。
传统Dijkstra算法中主要操作有:

  1. 每次从QQ集中找到LkL_k最小的点,最坏情况下需:

    (V−1),(V−2),⋯,1

    (V-1),(V-2),\cdots,1
    次操作。
    所以整个算法过程共需:

    (V−1)+(V−2)+⋯+1=(v−1)v2=V22−12

    (V-1)+(V-2)+\cdots+1=\frac{(v-1)v}{2}=\frac{V^2}{2}-\frac{1}{2}
    次操作。

  2. 计算并更新各点邻居的LkL_k值,实质上是将所有的边遍历一遍,故需EE次操作。
    则用大OO表示法:

    O(V22−12+E)⇔O(n2)

    O(\frac{V^2}{2}-\frac{1}{2}+E)\Leftrightarrow O(n^2)
    故传统Dijkstra算法的时间复杂度为O(n2)O(n^2)

Dijkstra算法的优化

在上述对于传统Dijkstra算法的时间复杂度分析中,我们可知,(尤其是稀疏图中)从QQ集中找到LkL_k最小的点的过程极大影响了算法的性能,这个过程在顶点无序状态下需要O(n2)O(n^2)的复杂度。
因此对于顶点的有效排序可以大大地提高算法的性能,常用的方法有:小顶堆或优先队列

  • 小顶堆优化中,我们初始将所有顶点设置为一个小顶堆,此时堆顶一定为起点,而在每一次迭代中,我们将堆顶元素取出(复杂度O(1)O(1)),而后调整小顶堆(复杂度O(logn)O(logn)),这样调整VV次,直到堆中所有顶点全部加入SS。则整个算法的时间复杂度将从O(n2)O(n^2)优化为O(nlogn)O(nlogn)。
  • 优先队列优化中,建立一个最小优先的优先级队列,队列中保存顶点和当前LkL_k值的二元组,初始将起点二元组入队,每当某个顶点的LkL_k值被更新,则将这个新的顶点二元组入队,每次迭代时,将队首元素取出并出队,直到队列为空。由于优先队列一般采用堆实现,故维护优先队列的复杂度同为O(logn)O(logn),则整个算法的时间复杂度同被优化为O(nlogn)O(nlogn)。

注意:优先队列优化中,新的顶点二元组入队时,旧的二元组依然在优先队列中,因此每次出队的元素可能会有杂音,如何识别并去除这些杂音是这种优化方式需要考虑的。

关于无向图

事实上,Dijkstra算法同样可以处理无向图中的单源最短路问题(无向图其实可看做一种特殊的有向图),但在这种情况下,要对算法做一些修改:标记已经访问过的边,在寻找邻居时不沿已标记过的边寻找

关于空间复杂度

Dijkstra算法的空间复杂度视具体实现方法而定,采用邻接矩阵的存图方式的空间复杂度为O(n2)O(n^2),然而在稀疏图中,这种存储方式将有大量的空间浪费,因此推荐使用邻接表和有关标志数组存图。

算法具体代码实现多种多样,留作读者自行思考,在此不再赘述。

绝对原创,转载请注明出处。
才疏学浅,如有错误,望不吝赐教

详解Dijkstra算法(含数学证明和优化)相关推荐

  1. 最短路径——Dijkstra算法以及二叉堆优化(含证明)

    一般最短路径算法习惯性的分为两种:单源最短路径算法和全顶点之间最短路径.前者是计算出从一个点出发,到达所有其余可到达顶点的距离.后者是计算出图中所有点之间的路径距离. 单源最短路径 Dijkstra算 ...

  2. ADMM,ISTA,FISTA算法步骤详解,MATLAB代码,求解LASSO优化问题

    ADMM,ISTA,FISTA算法步骤详解,MATLAB代码,求解LASSO优化问题 原创文章!转载需注明来源:©️ Sylvan Ding's Blog ❤️ 实验目的 了解 ADMM, ISTA, ...

  3. 详解线性回归算法的纯Python实现

    ↑↑↑关注后"星标"简说Python人人都可以简单入门Python.爬虫.数据分析 简说Python推荐 来源|天池大数据科研平台作者|黄佳 零基础学机器学习--一文详解线性回归算 ...

  4. 详解floyd算法 及<MATLAB>实现

    欢迎来到 < Haoh-Smile > 的博客,觉得受用客官就点个赞评论一下呗!** 详解floyd算法 及MATLAB实现 一.Floyd算法原理 Floyd算法是一个经典的动态规划算法 ...

  5. 区块链技术用解决拜占庭将军问题_两军问题_拜占庭将军问题详解图解算法

    两军问题 我们来看一下好处理器的情况,但通信线路有问题.这就是所谓的两军问题,可以概括如下: A,B 两军师协同攻击敌军C, A和B在物理上是分开的,并使用信使进行通信. A向B发送一个消息" ...

  6. 详解rsync算法--如何减少同步文件时的网络传输量

    详解rsync算法--如何减少同步文件时的网络传输量 先看下图中的场景,客户端A和B,以及服务器server都保存了同一个文件,最初,A.B和server上的文件内容都是相同的(记为File.1).某 ...

  7. 【算法知识】详解堆排序算法

    点击蓝色字关注我们! 什么是堆 「堆」首先是一个完全二叉树,「堆」分为「大顶堆」和「小顶堆」: 「大顶堆」 : 每个节点的值大于或等于其左右孩子节点的值,称为大顶堆. 「小顶堆」同理就是每个节点的值小 ...

  8. 【算法知识】详解基数排序算法

    已发布: [算法知识]详解选择冒泡算法 [算法知识]详解选择排序算法 [算法知识]详解插入排序算法 [算法知识]详解快速排序算法 [算法知识]详解归并排序算法 基本思想 基数排序的思想是将整数按位数切 ...

  9. 【算法知识】详解归并排序算法

    已发布: [算法知识]详解选择冒泡算法 [算法知识]详解选择排序算法 [算法知识]详解插入排序算法 [算法知识]详解快速排序算法 基本思想 归并排序的基本思想是: 先将序列一次次分成子序列,直到子序列 ...

  10. 【算法知识】详解快速排序算法

    基本思想 已发布: [算法知识]详解选择冒泡算法 [算法知识]详解选择排序算法 [算法知识]详解插入排序算法 本文的思路是以从小到大为例讲的. 快速排序的基本思想是任取待排序序列的一个元素作为中心元素 ...

最新文章

  1. Handler线程间通信
  2. OpenWRT(基于LEDE17.01.4)Open***的Client与Server端内网互通
  3. mysql创建只读权限用户_新品速递 | Harbor 修复权限提升漏洞,MySQL Plus 支持密码强度校验以及审计功能...
  4. java 流拆分_java - 在Java流中拆分字符串? - SO中文参考 - www.soinside.com
  5. 百度android输入法表情符号,百度输入法安卓5.8版推“史上最丰富颜文字库”
  6. android里build报错怎么办,Android Studio 当build时候出错解决办法
  7. 手把手教你训练一个秒杀科比的投篮AI,不服来练 | 附开源代码
  8. wing ide 3.x 中文设置
  9. Java 异常Exception e中e的getMessage()和toString()以及 e.printStackTrace();方法的区别
  10. faster rcnn fpn_Faster-RCNN详解和torchvision源码解读(三):特征提取
  11. IDEA+Java+SSM+Mysql+Bootstrap+Maven实现网上书城系统
  12. 送给大家一个很好的Web前端开发工具
  13. IDEA 各版本下载地址
  14. FMS3.5的安装使用
  15. linux 用户搬家后安装的软件,linux软件搬家
  16. 名家名言 Chuck Thacker
  17. 原生JS 简单购物车网页
  18. eclipse 项目中搜索资源(类方法,文件名,文件中的字符串)
  19. Cocos2d-x游戏暂停、继续游戏、重新开始界面的实现---之游戏开发《赵云要格斗》(10)
  20. 深入了解DataGridView控件

热门文章

  1. WEB 服务器调试利器 -- Tamper Data
  2. 数学归纳法·Fibonacci数列
  3. IPV4地址详细解释
  4. 浪潮FS6700 思科MDS 9148S光纤通道交换机图形化配置方法,小白也能轻松上手
  5. 路由器选华硕还是tp_家用选TP-LINK路由器好还是华为路由器好
  6. win10系统的 3Dsmax2020 安装失败报错 1603 解决方案 亲测可用
  7. 毁灭者DC W650DC装黑苹果心得
  8. 今天是本学期的第几周的第几天? (15 分) C语言
  9. 高效能人士的七个习惯(零)前言
  10. 【闲置路由器的有效利用】路由器有线桥接实现无线漫游