最近朋友问了一个关于列车调度的问题,求两个地点之间的最短路径。听起来挺简单的问题,但是仔细思考后发现彻底无从下手。最近空闲下来便恶补了一番数据结构。php
求最短路径的方法有Dijkstra,Floyd,BFS等等 其中Floyd适合多源最短路径,BFS适合无权值的状况,这里问题属于单源最短路径,因此咱们采用Dijkstra算法.git
开干吧!拿出地铁卡就是一顿画,如今咱们就把问题抽象为求宝安中心-老街的最短路径github
开始以前首先咱们介绍几个概念算法
对于这种起点到某个顶点的真正的最短路径,咱们称它为全局最短路径。已经找到全局最短路径的顶点,咱们将其储在found集合中.如今来初始化一下foundbash
// 初始时,咱们已知的就只有宝安中心到宝安中心的最短路径
$found = ['宝安中心'];
复制代码
用来存储起点(宝安中心)到某个顶点u, 相对于S的最短路径。通俗解释就是:宝安中心到世界之窗通过了新安,那么必须有新安∈ S。 不然咱们没法直接知道其相对最短路径,在dist中则将其记为不可能存在的大值,用来表示这个点咱们目前尚未办法探索到。数据结构
dist中存储的最短路径称之为相对于S的最短路径。下文咱们简称为相对最短路径,和其对应的咱们有一个全局最短路径spa
初始一下dist集合3d
const MAX = 65525; //65525是咱们定义的一个不可能出现的大值
$dist = [
'深圳北站' => 5,
'新安' => 8,
'世界之窗' => MAX,
'福田' => MAX,
'购物公园' => MAX,
'老街' => MAX,
'布吉' => MAX,
];
复制代码
开始咱们的算法~ 咱们首先找到dist
中的最小值,这里是 深圳北站 => 5
。code
顺便告诉你一个激动人心的消息,咱们找到了宝安中心到深圳北站的全局最短路径。cdn
what?为何dist中的最小值就是咱们要找的全局最短路径(这里是
宝安中心-深圳北站
)? 咱们要找的不是宝安中心-老街
的全局最短路径吗,知道了宝安中心-深圳北站
的最短路径有什么用吗? 我如今无法给你一个很好的解释,咱们继续往下看。
继续进行算法,下面是红色顶点表示已经肯定了全局最短路径的顶点
$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了
人眼扫描后能够肯定下一个全局最短路径的顶点为新安。而且有了新安的中转,咱们能够再次拓宽咱们的视野
为了表示清晰,对于尚未探索到相对最短路径,先隐藏其权重值
$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中最小值时, 找到了咱们的目标,老街。
算法描述完毕!
为何dist集合中的最小值就是咱们要找的全局最短路径?
$dist = [
'福田' => 10, // ok
'购物公园' => 12,
'老街' => 15,
];
复制代码
以某一次dist集合的部分数据为例子,按照算法描述 宝安中心-福田-购物公园
是咱们要找的全局最短路径。如今咱们假设到宝安中心-购物公园
存在更短的路径,则存在以下两种状况
红色区域表明集合found,表示已经找到了全局最短路径的顶点集合。 上面提到过,dist集合中存储的是相对于S的最短路径。
对于这种状况,算法在进行dist集合更新操做的时候就已经判断了宝安中心-福田-购物公园
和宝安中心-X-购物公园
之间的更小值,所以这种状况不可能存在。咱们继续来看另一种更加可能出现的状况
是否会存在这样一条最短路径呢?由于y-购物公园
的距离咱们并无探索过,因此这种状况是须要慎重思考一种状况。
先让时光倒流
此时咱们的dist集合中一共有5个顶点。其中宝安中心,福田,X已经被加入到了found集合中。Y经过X的中转后被发现,购物公园经过福田中专后被发现。 此时根据咱们的算法,将会在Y和购物公园中选取一个最小值,做为下一个全局最短路径顶点。这里算法选择了购物公园。说明宝安中心-福田-购物公园 < 宝安中心-X-Y
回到状况2
在有了 宝安中心-福田-购物公园 < 宝安中心-X-Y
前提下。 宝安中心-X-Y-购物公园 < 宝安中心-福田-购物公园
是否可以成立呢?假如等式不成立,则说明不可能存在一条比宝安中心-福田-购物公园
更短的全局最短路径。
等式是否成立我相信你一目了然。
正确性分析完毕!
你可能还在惊讶于dijkstra算法为何这么神奇?就算咱们已经知道了算法步骤,分析了算法的正确性。可仍是不由会感叹,究竟是怎么作到的,究竟是怎么找到最优解的?
回过头去看看算法描述你会发现,其实dijkstra并不知道本身何时可以找到本身想要的目标,它只是关注于眼前的最优解,而后碰巧在某一时刻眼前的最优解就是要寻找的目标值。这看起来有点笨,可是在某些状况十分有用,好比路由寻址中查找最短路径必需要用到这种策略。
哦~对了,这种只关注于眼前最优解的方法其实有个更加有逼格的名字 —— 贪心算法。