最近朋友問了一個關於列車調度的問題,求兩個地點之間的最短路徑。聽起來挺簡單的問題,但是仔細思考後發現徹底無從下手。最近空閒下來便惡補了一番數據結構。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 = ['寶安中心', '深圳北站'];
複製代碼
集合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以下爲了表示清晰,對於尚未探索到相對最短路徑,先隱藏其權重值
$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並不知道本身何時可以找到本身想要的目標,它只是關注於眼前的最優解,而後碰巧在某一時刻眼前的最優解就是要尋找的目標值。這看起來有點笨,可是在某些狀況十分有用,好比路由尋址中查找最短路徑必需要用到這種策略。
哦~對了,這種只關注於眼前最優解的方法其實有個更加有逼格的名字 —— 貪心算法。