前言
Dijkstra算法是應用于圖中單源最短路徑的搜索。我在這記錄下我在學習該算法時的一些想法、理解與總結。首先我會寫一段預備知識,以便于之后的理解。
算法基礎
- 選擇點到其他點的最短距離
- 局部最優是全局最優的充分必要條件
基礎1:
在一張權值都是 同號 的圖中,假設存在:①點A;②與A點相連的最短弧的弧頭C;那么路徑(A,C)為點A到點C的最短路徑。
圖1
如圖所示,(A,C) 是點A到點C的最短路徑。
解釋:在權值都為正的情況下,沒走一個點,其路徑只會增加。所以與點A相接最短的弧是抵達弧頭的最短路徑。
正文
集合{S}:用來存放已尋找到最短路徑的點。
集合{V}:全集
集合{V - S} 剩下的未找到最短路徑的點集
如果集合{S}包括了所有的點,那么圖的單源最短路徑尋找結束。
算法開始的時候集合{S}中只包括原點,Dijkstra算法的過程是逐漸填充集合{S}的過程。
怎么填充?
在集合{V - S}中找一個距離{S}最近的點,命其為 點V 將其加入集合{S}中。
解釋如圖:
圖2
注意:
①這張圖和前面那張圖沒有任何聯系;
②圖中的虛線并不是真實存在的線,虛線上的數組不是點至原點的路徑長度,而是集合{S}到點的路徑長度,這些數值的需要計算;
在填充的過程中,會發現一個問題。每添加一個點之后,集合{S} 至集合{V - S}中點的最短距離可能會發生變化。
例如 圖2 :添加點C之后,集合{S}到點B的最短距離變為先經過點C,在到達點B。即 5+4 < 10。因此再每加入一個點后就要時刻更新集合{S} 至 集合{V - S}中點的最短距離。
總結:Dijkstra算法一共需要三步
- ① 創建集合{S},將原點加入集合{S}。計算集合{S}到各點的距離,并儲存。
- ② 尋找目前距離集合{S}最短距離的 點V ,將 點V 加入集合{S}
- ③ 更新集合{V - S} 中的點距離 集合{S} 的距離
應用
#define MAX_SIZE 100
int route[MAX_SIZE][MAX_SIZE];//路徑
int curDis[MAX_SIZE];//當前最短路徑
int pre[MAX_SIZE];//前驅
bool Set[MAX_SIZE];//集合{S}
int N;//結點個數
void shortestPath(const int start, const int dest)
{
//初始化
fill(Set, Set+N, false);
fill(curDis, curDis+N, INF);
int i;
for(i = 0 ; i < N ; i++) pre[i] = i;
curDis[start] = 0;
//遍歷除原點外的頂點,因為每次循環都會尋找到一個可以加入集合{S}中的點,
for(i = 0 ; i < N ; i++){
//尋找 {V-S} 到原點最短路徑的點
int v = -1, MIN = INF;
for(int j = 0 ; j < N ; j++){
if(!Set[j] && curDis[j] < MIN)
v = j;
min = curDis[j];
}
}
if(v == -1) break;//有些結點無法到達
Set[v] = true;
//更新 {V-S} 中的最短路徑
for(int j = 0 ; j < N ; j++){
if(!Set[j] && route[u][j] != INF){
if(curDis[v] + route[v][j] < curDis[j]){
curDis[j] = curDis[v] + route[v][j];
pre[j] = u;//設置前驅
}else if(distance == shortestDistance[iY]){
//這里處理第二選擇情況
}
}
}
}
}
注意:權值必須是全是正的,或者全是負的。兩種情況的結果會不同,我只做在權值全為正的情況。如果想求最大路徑,可以將正權值都制負,然后使用Dijkstra算法尋找最小值。然后再去絕對值,則是最大路徑。