在網圖和非網圖中,最短路徑的含義不一樣。非網圖中邊上沒有權值,所謂的最短路徑,其實就是兩頂點之間通過的邊數最少的路徑;而對於網圖來講,最短路徑,是指兩頂點之間通過的邊上權值之和最少的路徑,咱們稱路徑上第一個頂點是源點,最後一個頂點是終點。算法
咱們講解兩種求最短路徑的算法。第一種,從某個源點到其他各頂點的最短路徑問題。數組
1,迪傑斯特拉(Dijkstra)算法spa
迪傑斯特拉算法是一個按路徑長度遞增的次序產生最短路徑的算法,每次找到一個距離V0最短的點,不斷將這個點的鄰接點加入判斷,更新新加入的點到V0的距離,而後再找到如今距離V0最短的點,循環以前的步驟。有些相似以前的普里姆算法,是針對點進行運算,貪心算法。code
這裏咱們首先約定,Vi在vertex[]中存儲的index = i,以方便闡述思路。思路以下:blog
(1)咱們拿到網圖,以下.咱們要找到從V0到V8的最短路徑,使用鄰接矩陣存儲的圖結構。咱們尋找距離V0最近的頂點,找到了鄰接點V1,距離是1,將其連入最短路徑。class
(2)修正與V1直接相關聯的全部點到V0的最短路徑。好比本來V2到V0的路徑是5,如今經過V1,將其修改成1+3 = 4.,將V4置爲6,V3置爲8.咱們如今就須要一個數組來存儲每一個點到V0的最短路徑了。咱們設置一個數組ShortPathTable[numVertex]來存儲。test
同時咱們還須要存儲每一個點到V0的最短路徑,爲此咱們設置一個數組Patharc[numVertex],P[i] = k表示Vi到V0的最短路徑中,Vi的前驅是Vk。咱們還須要一個數組來存儲某個頂點是否已經找到了到V0的最短路徑,爲此咱們設置finals[numVertex]。此時循環
S[] = { 0 , 1 , 4 , 8 , 6 , I , I , I , I } P[] = { 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 0 } f[] = { 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }
(3)此時拿到與V0距離最短的點(尚未加入最短路徑的,即final[i] = 0的點),這裏是V2,而後更新V2的鄰接點V4,V5到V0的最短路徑分別爲1+3+1=5和1+3+7=11,並將V2加入最短路徑遍歷
S[] = { 0 , 1 , 4 , 8 , 5 , I , I , I , I } P[] = { 0 , 0 , 1 , 1 , 2 , 0 , 0 , 0 , 0 } f[] = { 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 }
接下來的步驟跟前面相似,我就再也不重複了,你們應該也理解迪傑斯特拉算法的步驟了,下面來看代碼實現。im
public void ShortestPath_Dijkstra(){ int[] ShortPathTable= new int[numVertex]; int[] Patharc = new int[numVertex]; int[] finals = new int[numVertex]; //初始化 for (int i = 0; i < numVertex; i++){ ShortPathTable[i] = edges[0][i]; //初始化爲v0的鄰接邊 Patharc[i] = 0; finals[i] = 0; } finals[0] = 1; //v0無需找本身的鄰邊 int minIndex = 0; //用來保存當前已經發現的點中到V0距離最短的點 for (int i = 1; i < numVertex; i++){ //找到目前距離V0最近的點 int min = INFINITY; for (int index = 0; index < numVertex; index++){ if (finals[index] != 1 && ShortPathTable[index] < min){ minIndex = index; min = ShortPathTable[index]; } } finals[minIndex] = 1; //更新還未加入最短路徑的點到V0的距離 for (int index = 0; index < numVertex; index++){ if (finals[index] != 1 && (ShortPathTable[index] > (min + edges[minIndex][index]))){ ShortPathTable[index] = min + edges[minIndex][index]; Patharc[index] = minIndex; } } } //輸出最短路徑 int s = 8; System.out.println(8); while ( s != 0){ s = Patharc[s]; System.out.println(s); } }
其實根據這個算法獲得了v0到任意一個頂點的最短路徑和路徑長度的。時間複雜度O(n方)。
若是咱們想獲得v1,或者v2等到任意一個頂點的最短路徑呢?咱們要再跑一次這個算法才能獲得這樣複雜度就達到了O(n3),這是咱們所不能接受的。下面咱們來介紹一種直接獲得每個頂點到另一個頂點的最短路徑。
2,弗洛伊德(Floyd)算法
弗洛伊德算法的想法是,若是V1到V2的距離,大於V1到V0再從V0到V2的距離,那麼就把V1到V0的權值的拷貝,改成V1到V0+V0到V2. 同時把儲存的最短路徑也改掉,直到網中的每一條邊都被遍歷。
咱們用二維矩陣ShortPathTable存儲行號到列號的最短路徑的權值之和(初始化爲鄰接矩陣);Patharc存儲行號到列號的最短路徑中列號元素的前驅。初始化以下
舉例以下:
ShortPathTable矩陣: Patharc矩陣:
(1)從V0進入網圖中,此時沒有什麼須要變化的。
(2)到達V1,讓全部的頂點的路徑都從V1通過,發現V0到V2的路徑大於先到V1再到V2的路徑,因此將S矩陣和P矩陣中的相應位置更改。同理S[0][3] = 8, S[0][4] = 6。
後面同理,依次循環V2~V8,針對每一個頂點做爲中轉獲得計算結果。這樣咱們的最短路徑就完成了。
實現代碼以下:
public void ShortestPath_Floyd(){ int[][] ShortPathTable = new int[numVertex][numVertex]; int[][] Patharc = new int[numVertex][numVertex]; //初始化兩個矩陣 for (int row = 0; row < numVertex; row++){ for (int col = 0; col < numVertex; col++){ ShortPathTable[row][col] = edges[row][col]; Patharc[row][col] = col; } } for (int path = 0; path < numVertex; path++){ for (int row = 0; row < numVertex; row++){ for (int col = 0; col < numVertex; col++){ if (ShortPathTable[row][col] > (ShortPathTable[row][path] + ShortPathTable[path][col])){ ShortPathTable[row][col] = (ShortPathTable[row][path] + ShortPathTable[path][col]); Patharc[row][col] = Patharc[row][path]; } } } } //打印看結果 for (int row = 0; row < numVertex; row++) { for (int col = 0; col < numVertex; col++) { System.out.print(ShortPathTable[row][col] + "\t"); } System.out.println(); } System.out.println("***********************************"); for (int row = 0; row < numVertex; row++) { for (int col = 0; col < numVertex; col++) { System.out.print(Patharc[row][col] + "\t"); } System.out.println(); } }
結果:
0 1 4 7 5 8 10 12 16 1 0 3 6 4 7 9 11 15 4 3 0 3 1 4 6 8 12 7 6 3 0 2 5 3 5 9 5 4 1 2 0 3 5 7 11 8 7 4 5 3 0 7 5 9 10 9 6 3 5 7 0 2 6 12 11 8 5 7 5 2 0 4 16 15 12 9 11 9 6 4 0 *********************************** 0 1 1 1 1 1 1 1 1 0 1 2 2 2 2 2 2 2 1 1 2 4 4 4 4 4 4 4 4 4 3 4 4 6 6 6 2 2 2 3 4 5 3 3 3 4 4 4 4 4 5 7 7 7 3 3 3 3 3 7 6 7 7 6 6 6 6 6 5 6 7 8 7 7 7 7 7 7 7 7 8
咱們能夠發現,P矩陣的第一列與迪傑斯特拉算法的結果是同樣的。它的時間複雜度是三次方的,但它能夠解決多個頂點到多個頂點的最短路徑問題,比一樣場景下的迪傑斯特拉算法要省時得多。
上面是兩個算法對於無向圖的應用,實際上對於有向圖,也是一樣的使用效果。由於無向圖和有向圖的區別僅僅是鄰接矩陣是否對稱而已。