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並不知道本身何時可以找到本身想要的目標,它只是關注於眼前的最優解,而後碰巧在某一時刻眼前的最優解就是要尋找的目標值。這看起來有點笨,可是在某些狀況十分有用,好比路由尋址中查找最短路徑必需要用到這種策略。

哦~對了,這種只關注於眼前最優解的方法其實有個更加有逼格的名字 —— 貪心算法。

相關文章
相關標籤/搜索