上篇博客咱們詳細的介紹了兩種經典的最小生成樹的算法,本篇博客咱們就來詳細的講一下最短路徑的經典算法----迪傑斯特拉算法。首先咱們先聊一下什麼是最短路徑,這個仍是比較好理解的。好比我要從北京到濟南,而從北京到濟南有好多條道路,那麼最短的那一條就是北京到濟南的最短路徑,也是咱們今天要求的最短路徑。git
由於最短路徑是基於有向圖來計算的,因此咱們仍是使用上幾篇關於圖的博客中使用的示例。不過咱們今天博客中用到的圖是有向圖,因此咱們要講上篇博客的無向圖進行改造,改爲有向圖,而後在有向圖的基礎上給出最小生成樹的解決方案。本篇博客咱們的思路與以前數據結構相關博客的風格保持一致,首先咱們先給出迪傑斯特拉算法的原理以及詳細的示意圖,而後根據這些示意圖給出具體的實現方案。github
1、迪傑斯特拉算法原理解析算法
在博客的第一部分,咱們不談任何與代碼相關的內容,只談迪傑斯特拉算法的原理以及生成最短路徑的具體步驟。下方咱們會給出迪傑斯特拉算法的計算最短路徑的每一步,並給出每一步具體的說明。廢話少說,進入本部分的主題。數組
一、有向帶權圖數據結構
本篇博客依然採起咱們以前用的圖的結構。不過咱們本篇博客使用的是有向圖。下方這張圖就是是咱們以前使用的無向圖轉換過來的,只是給以前的圖的邊添加的具體的方向,其餘的併爲改動。由無向圖轉換後的有向圖以下所示,咱們將在下方的圖的基礎上計算出從A到D的最短路徑。測試
2.迪傑斯特拉算法的具體步驟spa
下圖就是求上圖中A->D的最短路徑時使用迪傑斯特拉算法的具體步驟。迪傑斯特拉算法主要核心思想是由起始結點開始,慢慢的由尾結點擴散。直到擴展到尾結點位置。下方咱們將根據每一個步驟給出具體的介紹:3d
(1)與起始結點A直接相連的點是B和F, 即 A->B的距離爲10, A->F的距離爲11, 因此咱們選擇A->B這個路徑。
(2)選擇B結點後,咱們開始探測與B相連的結點,即 A->B->G路徑長度爲26, A->B->I路徑長度爲22, A->B->C路徑長度爲28,這三個與上一步留下的 A->F(11)相比,A->F(11)的距離最短,因此接下來要探測與F相連的結點。
(3)F可到達的點是G和E,因此 A->F->G的距離爲27, A->F->E的距離爲37。由於 A->F->G(27) 大於 A->B->G(26)這個路徑,因此由A到G的路徑咱們依然選擇A->B->G(26)這個路徑。從 A->B->G(26),A->B->I(22),A->B->C(28), A->F->E(37)。中選出最短那個距離就是 A->B->I(22),因此咱們將I做爲咱們下次探測的結點。
(4)與I相連的就是D結點,因此咱們容易計算出 A->B->I->D這條路徑的長度爲 22+21=43。從 A->B->I->D(43), A->B->G(26), A->B->C(28), A->F->E(37)這些路徑中咱們仍是選擇最小的那一個,不難看出 A->B->G(26)這條路徑在上述路徑集合中最小,因此咱們將G結點做爲下次路徑的探測對象。
(5)G可到達的結點是H和D, 因此咱們能夠獲得兩條新的路徑 A->B->G->H(26+19=45)和 A->B->G->D(26+24=50),由於 A->B->G->D(50)這條路徑的長度要大於 A->B->I->D(43)這條路徑的長度,由於這兩條路徑都是從A到D,因此咱們選擇較小的 A->B->I->D(43)路徑。從 A->B->G->H(45),A->B->I->D(43), A->B->C(28), A->F->E(37)這幾條路徑中咱們依然選擇最小的那條路徑。從上面這幾條數據咱們不難看出 A->B->C(28)這條路徑最短,因此咱們下次要探測的點是C點。
(6)C點能夠到達的點只有D點,因此咱們能夠獲得一條新的路徑 A->B->C->D, 路徑長度爲50。由於從A到達D的路徑還有 A->B->I->D(43),而 A->B->C->D(50)這條路徑的長度要大一些,A到D點的路徑依然選擇 A->B->I->D(43)。C結點探測完畢,咱們從 A->B->G->H(45),A->B->I->D(43), A->F->E(37)幾條候選路徑中依然選擇最小的那一個。不難看出 A->F->E(37)這條路徑最小,因此下一步咱們要探測E結點。
(7)E結點能夠到達的點爲H和D,因此 A->F->E(37)這條路徑能夠延伸爲 A->F->E->H(44)和A->F->E->G(57)兩條邊。由於A到H的路徑還有一條爲 A->B->G->H(45),而咱們剛生出的這一條要小於以前的那一條,因此A到H的路徑更新爲 A->F->E->H(44)。而A->F->E->G(57)這條A到D的路徑要比 A->B->I->D(43)要大,因此不進行更新,A到D的路徑依然採用A->B->I->D(43)。通過這一步後咱們將 A->F->E->H(44)和A->B->I->D(43)進行比較,較小的路徑爲 A->B->I->D(43),而D節點又是咱們的終點, 因此A到D的最短路徑爲A->B->I->D(43)。
三、迪傑斯特拉算法的數據表示對象
上面是咱們使用圖形的方式給出了迪傑斯特拉算法的具體步驟,接下來咱們會把上面的步驟轉換成數據的表示方式,以便於咱們使用程序進行計算。下方就是上面這些示意圖的的完整的數據表示。下方矩陣中的數據標誌着起始節點到該節點的距離。由上到下,以此增長距離的大小,而最上面一排的紅色字母,標示着該結點的前驅。下方咱們在給出每個行數據的詳細介紹。blog
第一行數據記錄了A->B和A->F的信息,固然A->A的距離爲0。其餘尚不可達的點爲-1。
第二行在第一行數據的基礎上證據了 A->B->C, A->B->G, A->B->I的距離信息,由於從第一行數的比較咱們能夠知道,A->B的距離要小於A->F的距離,因此咱們的最短路徑要向着A->B->?上發展。
第三行的數據則是在第二行的數據上獲得的,從第二行數據上咱們容易看出,A->F(11)要比A->B->(C, G, I)都要小,因此咱們的最短路徑要向着A->F->?的放心發展。因此咱們找到了A->F->E(37)和A->F->G(28)這條路徑。由於 A->B->G(26)小於A->F->G(28),因此A到G的路徑不進行更新。
第四行的數據則是第三行數據的延伸,咱們能夠知道A->B->I(2)在當前全部延伸的路徑中最小,因此咱們要向着 A->B->I->? 方向發展。所以咱們找到了 A->B->I->D(43)這條路徑。
以此類推……
其實上圖就是咱們以前原理圖的數據表示,接下來咱們就要根據這些原理圖和數據圖來給出咱們的代碼實現,固然咱們仍是使用Swift語言來實現。
2、迪傑斯特拉算法的具體實現
1.上述原理總結
上面說這麼多,簡單的總結一下,上面整個過程無非就是下面這兩步的循環,而循環結束的條件就是最短路徑延伸到結束路徑便可,也就是咱們本例中的D點。
第一步:比較已經發展的全部路徑,找出目前最短的路徑。
第二步:在上一步找到的最短路徑的基礎上發展新的路徑,而後重複上一步,直到延伸到 end節點爲止。
通過這麼一分析,在給出具體的代碼實現就顯得簡單多了,咱們的程序總體上來講就是一個循環,裏邊包含着上面這兩步。
2.具體代碼實現
分析完原理後,接下來咱們就要開始實現咱們的代碼了。下方就是咱們整個代碼的具體實現。固然,咱們的圖結構是以鄰接鏈表的形式存儲的有向聯通圖。具體代碼實現以下所示:
(1)路徑結構的存儲
咱們先建立一個DistanceInfo類,該類中記錄了一些距離信息。previousNoteIndex字段存儲的是當前節點的前驅節點的索引,默認爲-1。而distance存儲的就是起始結點到該節點的距離,默認是最大距離。而isInPath則標記該結點是否位於已生成路徑的上,若是是的話就是true, 若是不是就是false,默認是false。
(2)代碼的總體結
下方代碼塊就是咱們迪傑斯特拉算法代碼實現的總體結構。首先咱們會建立一個distanceInfos數組,數組中元素的個數就是圖的結點的個數。其中存儲的是DistanceInfo的對象,每一個數組索引對應的DistanceInfo對象中存儲的信息就是起始結點到該結點的距離信息。首先咱們對起始結點的DistanceInfo對象進行初始化。
緊接着下方這個循環就是咱們上面所說的循環,循環結束的條件就是將最短路徑延伸到end結點。循環中主要有兩步,第一步是在當前循環的index對應的結點上發展新的路徑,第二步就是在這些發展的路徑中尋找最短路徑,在這個新最短路徑的基礎上再次發展新的路徑。
(3)發展新的路徑
接下來咱們來看一下countCurrentNoteAllDistance()方法中的代碼,也就是發展新的路徑的代碼。主要就是遍歷鄰接鏈表上當前索引所連的鏈表上的數據,將這些數據所對應的結點添加到咱們的路徑上。往路徑上添加新的結點是要注意一點,好比A->D是100, 以前B->D是80,若是如今的路徑要比以前的路徑要大就不更新,反之就更新咱們距離信息數組中的DistanceInfo對象。具體代碼以下所示:
(4)、尋找最小路徑
下方代碼就是尋找當前已發展的全部路徑中最短的那條路徑,主要仍是對DistanceInfo數組的操做。下方代碼,找到最短路徑後,並把最短路徑的索引進行返回。
上方就是咱們迪傑斯特拉算法代碼實現的核心代碼。
3、測試用例
實現完代碼後,咱們就要對代碼進行測試了。下方就是咱們的測試用例,該測試用例中建立的圖是有向連通圖,而且要求出節點A->D的最短路徑。
上述測試用例的輸入結果以下:
今天的博客就先到這兒,本篇博客所涉及的Demo也將會在github上進行分享,分享地址以下:
github分享地址:https://github.com/lizelu/DataStruct-Swift/tree/master/ShortestPathDijkstra