最短路DijkStra’s Algorithm算法詳解
dijkstra(圖解)
概念:
Weight[m,n]: 二維數組,代表節點m到節點n的權重,即圖上每條邊的權重值.
WeightMin[n]: 一維數組,代表從開始節點0到節點n的已知通路上,所有已計算的權重之和的最小值.用來存放每一次計算的最小值.
FinalSet:已經確認的最終節點的集合
圖上數據說明: 節點從左上點到右下點,從左到右從上到下,座標從0開始到8
Weight[m,n]數據如下
n0 | n1 | n2 | n3 | n4 | n5 | n6 | n7 | n8 | |
---|---|---|---|---|---|---|---|---|---|
n0 | 2 | 9 | 6 | ||||||
n1 | 1 | ||||||||
n2 | 4 | 6 | |||||||
n3 | 4 | ||||||||
n4 | 2 | 9 | 7 | ||||||
n5 | 5 | 1 | |||||||
n6 | 5 | ||||||||
n7 | 1 | 5 | |||||||
n8 |
算法基本原理
基礎原理其實很直白樸素,就是從開始節點起,計算所有能到達終止節點的通路的距離。從這些通路中找尋權重和最小的那一條通路。
不過這種樸素的找法隨着節點數增加由於組合爆炸的原因,計算複雜度是呈階乘級上升的,比指數級還恐怖.
然後在這個原理的基礎上做一下優化,因爲對於到達之前某個節點的最短距離已經確定了之後,我們可以該節點的最短距離存起來,當計算它之後的節點通路時,就直接拿出來用,而不需要再重頭計算之前的那些節點。
優化後的算法只做2件事
【P1】第一,不斷運行【廣度優先算法】,優先找到最接近源點的所有可見點(能與已知節點直接連通的點),計算這些可見點到源點的距離長度,由於一個節點可能有多條通路,所以當計算該點到源點的另一條通路的距離長度時,要與之前的WeightMin[n]取最小值然後再更新。
【P2】第二,每當運行完單次【廣度優先算法】後,使用【貪心算法】,從所有已知路徑中選擇長度最短的那條路徑,將頂點歸入最終節點集合裏FinalSet裏,然後再從該節點運行【廣度優先算法】,直至圖裏所有點都進入FinalSet裏。
【貪心算法】保證了進入FinalSet裏的節點距離一定是能到達該點的所有路徑中最短的,不可能有其他到達該點更短的距離。
【廣度優先算法】保證了在每次選擇路徑時,都能找到最短的一條路徑。
流程開始:
圖1
將所有節點權重和WeightMin[]更新爲無窮大,暫時以99代替(因爲所有節點權重和都沒有比99更大的,可以用來代替無窮大)。
【P1】初始條件,從自己到自己的權重爲0,WeightMin[0]=0,其餘都是99。
【P2】將節點0歸入最終節點集合裏FinalSet裏,FinalSet[]={0},此時FinalSet未包含全部節點,所以即將對節點0運行【廣度優先算法】
圖2
【P1】對節點0運行【廣度優先算法】,計算其所有可見點(n1,n3,n4)這3點到源點的通路的距離長度(使用已知的WeightMin[0]加上其直接連接的各邊的權重Weight[0,(n1,n3,n4)]),分別是[0+2,0+9,0+6]。然後與之前的WeightMin[[1,3,4]]:[99,99,99]進行比較,取最小值(2<99,9<99,6<99)然後再更新,所以最後這3點的WeightMin[[1,3,4]]=[2,9,6]。
【P2】從所有已知路徑頂點n1(2),n3(9),n4(6)中找長度最短的那條的頂點n1(即節點1),將其納入最終節點集合FinalSet={0,1},此時FinalSet未包含全部節點,所以即將對節點1運行【廣度優先算法】
圖3
【P1】對節點1運行【廣度優先算法】,計算其所有可見點(n2,n4)到源點的通路的距離長度WeightMin[1]+Weight[1,(n2,n4)]=[2+1,2+3]。然後與之前的WeightMin[[2,4]:[99,6]進行比較更新,WeightMin[[2,4]]=[3,5]。
【P2】從所有已知路徑頂點n2(3),n3(9),n4(5)中找長度最短的那條的頂點n2,將其納入最終節點集合FinalSet={0,1,2},此時FinalSet未包含全部節點,所以即將對進來的節點2運行【廣度優先算法】
圖4
【P1】對節點2運行【廣度優先算法】,計算其所有可見點(n4,n6)到源點的通路的距離長度WeightMin[2]+Weight[1,(n4,n6)]=[3+1,3+6]。然後與之前的WeightMin[[4,6]:[5,99]進行比較更新,WeightMin[[4,6]=[4,9]。
【P2】從所有已知路徑頂點n3(9),n4(4),n6(9)中找長度最短的那條的頂點n4,將其納入最終節點集合FinalSet={0,1,2,4},此時FinalSet未包含全部節點,所以即將對進來的節點4運行【廣度優先算法】
圖5
【P1】對上次歸入FinalSet的節點運行【廣度優先算法】求所有可見點的距離長度
g(n4) => (n3,n5,n7)
w(n3,n5,n7)=WeightMin[4]+Weight[4,(n3,n5,n7)]=[4+2,4+9,4+7]=[6,13,11]
WeightMin[[3,5,7]] = min([6,13,11] ,WeightMin[[3,5,7]]) = min([6,13,11] ,[9,99,99])=[6,13,11]
【P2】從所有已知路徑頂點(=上一次全部頂點 - 上一次選中頂點 + 本次可見點)中選出長度最短的那條的頂點,歸入FinalSet
min{n3,n5,n6,n7} = min{WeightMin[[3,5,6,7]]}=min([6,13,9,11])=6
n3
FinalSet={0,1,2,3,4}
圖6
【P1】對上次歸入FinalSet的節點運行【廣度優先算法】求所有可見點的距離長度
g(n3) => (n7)
w(n7) = WeightMin[3]+Weight[3,(n7)] = [6+4] = [10]
WeightMin[7] = min([10] ,WeightMin[7]) = min([10] ,[11]) = [10]
【P2】在所有已知路徑中選出最短路徑對應的節點,歸入FinalSet
min{n5,n6,n7} = min{WeightMin[[5,6,7]]} = min([13,9,10]) = 9
n6
FinalSet={0,1,2,3,4,6}
圖7
【P1】對上次歸入FinalSet的節點運行【廣度優先算法】求所有可見點的距離長度
g(n6) => (n8)
w(n8) = WeightMin[6]+Weight[6,(n8)] = [9+5] = [14]
WeightMin[8] = min([14] ,WeightMin[8]) = min([14] ,[99]) = [14]
【P2】在所有已知路徑中選出最短路徑對應的節點,歸入FinalSet
min{n5,n7,n8} = min{WeightMin[[5,7,8]]} = min([13,10,14]) = 10
n7
FinalSet={0,1,2,3,4,6,7}
圖8
【P1】對上次歸入FinalSet的節點運行【廣度優先算法】求所有可見點的距離長度
g(n7) => (n5,n8)
w(n5,n8) = WeightMin[7]+Weight[7,(n5,n8)] = [10+1,10+5] = [11,15]
WeightMin[[5,8]] = min([11,15] ,WeightMin[[5,8]]) = min([11,15] ,[13,14]) = [11,14]
【P2】在所有已知路徑中選出最短路徑對應的節點,歸入FinalSet
min{n5,n8} = min{WeightMin[[5,8]]} = min([11,14]) = 11
n5
FinalSet={0,1,2,3,4,6,7,5}
圖9
【P1】對上次歸入FinalSet的節點運行【廣度優先算法】求所有可見點的距離長度
g(n5) => (n8)
w(n8) = WeightMin[5]+Weight[5,(n8)] = [11+1] = [12]
WeightMin[8] = min([12] ,WeightMin[8]) = min([12] ,[14]) = [12]
【P2】在所有已知路徑中選出最短路徑對應的節點,歸入FinalSet
min{n8} = min{WeightMin[8]} = min([12]) = 12
n8
FinalSet={0,1,2,3,4,6,7,5,8}
至此,所有節點都進入FinalSet裏