最短路徑問題
看了王道的視訊,感覺雲裡霧裡的,所以寫這個部落格來加深理解。(希望能在12點以前寫完)
一、總體思想
dijkstra演算法的主要思想就是基於貪心,找出從v開始的頂點到各個點的最短路徑,做法入下
1.初始化三個輔助陣列
s[],dist[],path[]
s[]:這個陣列用來標記結點的訪問與否,如果該結點被訪問,則為1,如果該結點還沒有訪問,則為0;
dist[]:這個陣列用來記錄當前從v到各個頂點的最短路徑長度,演算法的核心思想就是通過不斷修改這個表實現;
path[]:這個陣列用來存放最短路徑;
2.遍歷圖,修改上面的各項陣列,每次只找最短路徑,直到遍歷結束
二、程式碼實現
1 void dijkstra(Graph G, int v) 2 { 3 int s[G.vexnum]; 4 int dist[G.vexnum]; 5 int path[G.vexnum]; 6 for(int i = 0; i < G.vexnum; i++) 7 { 8 s[i] = 0; 9 dist[i] = G.edge[v][i]; 10 if(G.edge[v][i] == max || G.edge[v][i] == 0) 11 { 12 path[i] = -1; 13 } 14 else 15 { 16 path[i] = v; 17 } 18 s[v] = 1; 19 } 20 21 for(int i = 0; i < G.vexnum; i++) 22 { 23 int min = max; 24 int u; 25 for(int j = 0; j < G.vexnum; j++) 26 { 27 if(s[j] != 1 && dist[j] < min) 28 { 29 min = dist[j]; 30 u = j; 31 } 32 } 33 s[u] = 1; 34 for(int j = 0; j < G.vexnum; j++) 35 { 36 if(s[j] != 1 && dist[j] > dist[u] + G.edge[u][j]) 37 { 38 dist[j] = dist[u] + G.edge[u][j]; 39 path[j] = u; 40 } 41 } 42 } 43 }
三、程式碼解釋
先自己定義一個無窮大的值max
#define max inf
dijkstra演算法傳入的兩個參為
圖Graph G;
起點結點 int v;
首先我們需要三個輔助陣列
1 int s[G.vexnum];//記錄結點時是否被訪問過,訪問過為1, 沒有訪問過為0 2 int dist[G.vexnum];//記錄當前的從v結點開始到各個結點的最短路徑長度 3 int path[G.vexnum];//記錄最短路徑,存放的是該結點的上一個為最短路徑的前驅結點
初始化三個陣列
1 for(int i = 0; i < G.vexnum; i++) 2 { 3 s[i] = 0;//目前每個結點均未被訪問過,設為0 4 dist[i] = G.edge[v][i];//dist[]陣列記錄每個從v結點開到其他i結點邊的長度(權值) 5 if(G.edge[v][i] == max || G.edge[v][i] == 0) 6 { 7 path[i] = -1; 8 }//如果v到i不存在路徑或者i就是v結點時,將path[i]設為-1,意為目前v結點不存在路徑到i 9 else 10 { 11 path[i] = v; 12 }//反之,若v到i存在路徑,則v就是i的前驅結點,將path[i] = v 13 s[v] = 1;//從遍歷起點v開始,即已經訪問過頂點s[v]=1 14 }
開始遍歷陣列並且每次修改輔助陣列以記錄目前的情況,直至遍歷結束
1 for(int i = 0; i < G.vexnum; i++) 2 { 3 int min = max;//宣告一個min = max用來每次記錄這次遍歷找到的最短路徑的長度(權值) 4 int u;//宣告u來記錄這次歷找到的最短路徑的結點 5 for(int j = 0; j < G.vexnum; j++)//開始遍歷 找目前的最短路徑 6 { 7 if(s[j] != 1 && dist[j] < min) 8 { 9 min = dist[j]; 10 u = j; 11 }//找出v到結點j的最短路徑,並且記錄下最短路徑的結點u = j 12 } 13 s[u] = 1;//找到結點u,即已訪問過u,s[u] = 1 14 for(int j = 0; j < G.vexnum; j++)//開始遍歷 修改輔助陣列的值 15 { 16 if(s[j] != 1 && dist[j] > dist[u] + G.edge[u][j]) 17 { 18 dist[j] = dist[u] + G.edge[u][j]; 19 path[j] = u; 20 }//如果v→j的路徑比v →u→j長,那麼修改dist[j]的值為 dist[u] + G.edge[u][j],並且修改j的前驅結點為path[j] = u 21 } 22 }
遍歷結束後,陣列dist[]就是存放了起點v開始到各個頂點的最短路徑長度
最短路徑包含的結點就在path陣列中
例如我們得到如下的path[]陣列
1 path[0] = -1;//0到自己無前驅結點 2 path[1] = 0;//1的前驅為結點0,0無前驅結點,即最短路徑為0 →1 3 path[2] = 1;//2的前驅結為點1,1的前驅結點0,0無前驅結點,即最短路徑為0 →1 →2 4 path[3] = 0;//3的前驅為結點0,0無前驅結點,即最短路徑為0 →3 5 path[4] = 2;//4的前驅結為點2,2的前驅結為點1,1的前驅結點0,0無前驅結點,即最短路徑為0 →1 →2 →4
其實不難看出,每次都會標記已經訪問過的節點,訪問過的節點不會再更改,即 這樣的演算法不可逆,也就是dijkstra對於存在負權值的圖不適用,明天再更新Floyd演算法叭?