Dijkstra算法及正确性分析

最近朋友问了一个关于列车调度的问题,求两个地点之间的最短路径。听起来挺简单的问题,但是仔细思考后发现彻底无从下手。最近空闲下来便恶补了一番数据结构。php

求最短路径的方法有Dijkstra,Floyd,BFS等等 其中Floyd适合多源最短路径,BFS适合无权值的状况,这里问题属于单源最短路径,因此咱们采用Dijkstra算法.git

开干吧!拿出地铁卡就是一顿画,如今咱们就把问题抽象为求宝安中心-老街的最短路径github

开始以前首先咱们介绍几个概念算法

found集合

对于这种起点到某个顶点的真正的最短路径,咱们称它为全局最短路径。已经找到全局最短路径的顶点,咱们将其储在found集合中.如今来初始化一下foundbash

// 初始时,咱们已知的就只有宝安中心到宝安中心的最短路径
   $found = ['宝安中心'];
复制代码

dist集合

用来存储起点(宝安中心)到某个顶点u, 相对于S的最短路径。通俗解释就是:宝安中心到世界之窗通过了新安,那么必须有新安∈ S。 不然咱们没法直接知道其相对最短路径,在dist中则将其记为不可能存在的大值,用来表示这个点咱们目前尚未办法探索到。数据结构

dist中存储的最短路径称之为相对于S的最短路径。下文咱们简称为相对最短路径,和其对应的咱们有一个全局最短路径spa

初始一下dist集合3d

const MAX = 65525; //65525是咱们定义的一个不可能出现的大值

 $dist = [
     '深圳北站' => 5,
     '新安' => 8,
     '世界之窗' => MAX,
     '福田' => MAX,
     '购物公园' => MAX,
     '老街' => MAX,
     '布吉' => MAX,
 ];    
复制代码

算法描述

开始咱们的算法~ 咱们首先找到dist中的最小值,这里是 深圳北站 => 5code

顺便告诉你一个激动人心的消息,咱们找到了宝安中心到深圳北站的全局最短路径。cdn

what?为何dist中的最小值就是咱们要找的全局最短路径(这里是宝安中心-深圳北站)? 咱们要找的不是宝安中心-老街的全局最短路径吗,知道了宝安中心-深圳北站的最短路径有什么用吗? 我如今无法给你一个很好的解释,咱们继续往下看。

继续进行算法,下面是红色顶点表示已经肯定了全局最短路径的顶点

既然已经又找到了一个全局最短路径顶点,咱们就把它更新到 found集合中

$found = ['宝安中心', '深圳北站'];
复制代码

集合found中的元素增长了一枚后,咱们的视野变的宽广了。咱们能够经过深圳北站做为一个中转更新 dist这个相对最短路径集合了

经过深圳北站这个中转站咱们能够获得已下相对最短路径

宝安中心-深圳北站-新安 = 5 + 2 = 7 宝安中心-深圳北站-福田 = 5 + 5 = 10 宝安中心-深圳北站-布吉 = 5 + 10 = 15

如今能够立刻去替换咱们dist集合中的值了吗?别急,咱们须要的是相对最短路径,可不是什么阿猫阿狗就能进来的。因此咱们须要进行一个比较.

// 若是新的相对最短路径比原有的相对最短路径要小,咱们则进行一个更新
if ($newWeight < $dist['新安']) {
    $dist['新安'] = $newWeight;
}
复制代码

dist集合更新以下

$dist = [
     '深圳北站' => 5, // ok
     '新安' => 7, //8 -> 7
     '世界之窗' => MAX,
     '福田' => 10, // MAX -> 10
     '购物公园' => MAX,
     '老街' => MAX,
     '布吉' => 15 // MAX -> 15
 ];   
复制代码

如今咱们重复以前的步骤,找一个最小值,其就是咱们下一个全局最短路径。要记住,深圳北站就不要加入查找队列了,其已经被found了

人眼扫描后能够肯定下一个全局最短路径的顶点为新安。而且有了新安的中转,咱们能够再次拓宽咱们的视野

为了表示清晰,对于尚未探索到相对最短路径,先隐藏其权重值

更新后的 founddist以下

$found = ['宝安中心', '深圳北站','新安'];

$dist = [
     '深圳北站' => 5, // ok
     '新安' => 7, // ok
     '世界之窗' => 16, // MAX -> 16 = 9+7 = 宝安->新安 + 新安->世界之窗
     '福田' => 10, // MAX -> 10
     '购物公园' => MAX,
     '老街' => MAX,
     '布吉' => 15
 ]; 
 
复制代码

再次循环 (目标已经出如今咱们的视野中啦,别着急,咱们尚未肯定其全局最短路径) ↓

更新后的s和dist以下

$found = ['宝安中心', '深圳北站', '新安', '福田'];

$dist = [
     '深圳北站' => 5, // ok
     '新安' => 7, // ok
     '世界之窗' => 16,
     '福田' => 10, // ok
     '购物公园' => 12, // MAX -> 12
     '老街' => 15, // MAX -> 15
     '布吉' => 15
 ]; 
 
复制代码

再次循环↓

更新后的found和dist以下

$s = ['宝安中心', '深圳北站', '新安', '福田', '购物公园'];

$dist = [
     '深圳北站' => 5, // ok
     '新安' => 7, // ok
     '世界之窗' => 16,
     '福田' => 10, // ok
     '购物公园' => 12, // ok
     '老街' => 15,
     '布吉' => 15
 ]; 
 
复制代码

再次寻找dist中最小值时, 找到了咱们的目标,老街。

算法描述完毕!

算法实现

github.com/weiwenhao/a…

正确性分析

为何dist集合中的最小值就是咱们要找的全局最短路径?

$dist = [
     '福田' => 10, // ok
     '购物公园' => 12,
     '老街' => 15,
 ]; 
复制代码

以某一次dist集合的部分数据为例子,按照算法描述 宝安中心-福田-购物公园是咱们要找的全局最短路径。如今咱们假设到宝安中心-购物公园存在更短的路径,则存在以下两种状况

状况1

红色区域表明集合found,表示已经找到了全局最短路径的顶点集合。 上面提到过,dist集合中存储的是相对于S的最短路径。

对于这种状况,算法在进行dist集合更新操做的时候就已经判断了宝安中心-福田-购物公园宝安中心-X-购物公园之间的更小值,所以这种状况不可能存在。咱们继续来看另一种更加可能出现的状况

  • 状况2

是否会存在这样一条最短路径呢?由于y-购物公园的距离咱们并无探索过,因此这种状况是须要慎重思考一种状况。

先让时光倒流

此时咱们的dist集合中一共有5个顶点。其中宝安中心,福田,X已经被加入到了found集合中。Y经过X的中转后被发现,购物公园经过福田中专后被发现。 此时根据咱们的算法,将会在Y和购物公园中选取一个最小值,做为下一个全局最短路径顶点。这里算法选择了购物公园。说明宝安中心-福田-购物公园 < 宝安中心-X-Y

回到状况2

在有了 宝安中心-福田-购物公园 < 宝安中心-X-Y前提下。 宝安中心-X-Y-购物公园 < 宝安中心-福田-购物公园是否可以成立呢?假如等式不成立,则说明不可能存在一条比宝安中心-福田-购物公园更短的全局最短路径。

等式是否成立我相信你一目了然。

正确性分析完毕!

结语

你可能还在惊讶于dijkstra算法为何这么神奇?就算咱们已经知道了算法步骤,分析了算法的正确性。可仍是不由会感叹,究竟是怎么作到的,究竟是怎么找到最优解的?

回过头去看看算法描述你会发现,其实dijkstra并不知道本身何时可以找到本身想要的目标,它只是关注于眼前的最优解,而后碰巧在某一时刻眼前的最优解就是要寻找的目标值。这看起来有点笨,可是在某些状况十分有用,好比路由寻址中查找最短路径必需要用到这种策略。

哦~对了,这种只关注于眼前最优解的方法其实有个更加有逼格的名字 —— 贪心算法。

相关文章
相关标签/搜索