Dijkstra算法又稱為單源最短路徑,所謂單源是在一個有向圖中,從一個頂點出發,求該頂點至所有可到達頂點的最短路徑問題。
設G=(V,E)是一個有向圖,V表示頂點,E表示邊。它的每一條邊(i,j)屬於E,都有一個非負權W(I,j),在G中指定一個結點v0,要求把從v0到G的每一個接vj(vj屬於V)的最短有向路徑找出來(或者指出不存在)。
Dijstra算法是運用貪心的策略,從源點開始,不斷地通過相聯通的點找出到其他點的最短距離
基本思想是:
設置一個頂點的集合s,並不斷地擴充這個集合,一個頂點屬於集合s當且僅當從源點到該點的路徑已求出。開始時s中僅有源點,並且調整非s中點的最短路徑長度,找當前最短路徑點,將其加入到集合s,直到終點在s中。
基本步驟:
1、把所有結點分成兩組:
第一組:包括已經確定最短路徑的結點;
第二組:包括尚未確定最短路徑的結點。
2、開始時,第一組只包含起點,第二組包含剩余的點;
3、用貪心的策略,按最短路徑長度遞增的順序把第二組的結點加到第一組去,直到v0可達的所有結點都包含於第一組中。在這個過程中,不斷更新最短路徑,總保持從v0到第一組各結點的最短路徑長度dist都不大於從v0到第二組任何結點的路徑長度。
4、每個結點對應一個距離值,第一組結點對應的距離就是v0到此結點的最短路徑長度,第二組結點對應的距離值就是v0由第一組結點到此結點的最短路徑長度。
5、直到所有的頂點都掃描完畢(v0可達的所有結點都包含於第一組中),找到v0到其它各點的所有最短路徑。
動畫演示:http://www.jcc.jx.cn/kejiandb/Dijkstra.swf
如圖:求0點到其他點的最短路徑。
(1)開始時,s1={v0},s2={v1,v2,v3,v4},v0到各點的最短路徑是{0,10,&,30,100};
(2)在還未進入s1的頂點之中,最短路徑為v1,因此s1={v0,v1},由於v1到v2有路徑,因此v0到各點的最短路徑更新為{0,10,60,30,100};
(3)在還未進入s1的頂點之中,最短路徑為v3,因此s1={v0,v1,v3},由於v3到v2、v4有路徑,因此v0到各點的最短路徑更新為{0,10,50,30,90};
(4)在還未進入s1的頂點之中,最短路徑為v2,因此s1={v0,v1,v3,v2},由於v2到v4有路徑,因此v0到各點的最短路徑更新為{0,10,50,30,60};
數據結構:
(1)用一個二維數組a[i..j,i..j]來存儲各點之間的距離,10000表示無通路:
(2)用數組dist[i..j]表示最短路徑;
(3)用集合s表示找到最短路徑的結點。
1 /*************************************** 2 * About: 有向圖的Dijkstra算法實現 3 * Author: Tanky Woo 4 * Blog: www.WuTianQi.com 5 ***************************************/ 6 7 #include <iostream> 8 using namespace std; 9 10 const int maxnum = 100; 11 const int maxint = 999999; 12 13 // 各數組都從下標1開始 14 int dist[maxnum]; // 表示當前點到源點的最短路徑長度 15 int prev[maxnum]; // 記錄當前點的前一個結點 16 int c[maxnum][maxnum]; // 記錄圖的兩點間路徑長度 17 int n, line; // 圖的結點數和路徑數 18 19 void Dijkstra(int n, int v, int *dist, int *prev, int c[maxnum][maxnum]) 20 { 21 bool s[maxnum]; // 判斷是否已存入該點到S集合中 22 for(int i=1; i<=n; ++i) 23 { 24 dist[i] = c[v][i]; 25 s[i] = 0; // 初始都未用過該點 26 if(dist[i] == maxint) 27 prev[i] = 0; 28 else 29 prev[i] = v; 30 } 31 dist[v] = 0; 32 s[v] = 1; 33 34 // 依次將未放入S集合的結點中,取dist[]最小值的結點,放入結合S中 35 // 一旦S包含了所有V中頂點,dist就記錄了從源點到所有其他頂點之間的最短路徑長度 36 for(int i=2; i<=n; ++i) 37 { 38 int tmp = maxint; 39 int u = v; 40 // 找出當前未使用的點j的dist[j]最小值 41 for(int j=1; j<=n; ++j) 42 if((!s[j]) && dist[j]<tmp) 43 { 44 u = j; // u保存當前鄰接點中距離最小的點的號碼 45 tmp = dist[j]; 46 } 47 s[u] = 1; // 表示u點已存入S集合中 48 49 // 更新dist 50 for(int j=1; j<=n; ++j) 51 if((!s[j]) && c[u][j]<maxint) 52 { 53 int newdist = dist[u] + c[u][j]; 54 if(newdist < dist[j]) 55 { 56 dist[j] = newdist; 57 prev[j] = u; 58 } 59 } 60 } 61 } 62 63 void searchPath(int *prev,int v, int u) 64 { 65 int que[maxnum]; 66 int tot = 1; 67 que[tot] = u; 68 tot++; 69 int tmp = prev[u]; 70 while(tmp != v) 71 { 72 que[tot] = tmp; 73 tot++; 74 tmp = prev[tmp]; 75 } 76 que[tot] = v; 77 for(int i=tot; i>=1; --i) 78 if(i != 1) 79 cout << que[i] << " -> "; 80 else 81 cout << que[i] << endl; 82 } 83 84 int main() 85 { 86 freopen("input.txt", "r", stdin); 87 // 各數組都從下標1開始 88 89 // 輸入結點數 90 cin >> n; 91 // 輸入路徑數 92 cin >> line; 93 int p, q, len; // 輸入p, q兩點及其路徑長度 94 95 // 初始化c[][]為maxint 96 for(int i=1; i<=n; ++i) 97 for(int j=1; j<=n; ++j) 98 c[i][j] = maxint; 99 100 for(int i=1; i<=line; ++i) 101 { 102 cin >> p >> q >> len; 103 if(len < c[p][q]) // 有重邊 104 { 105 c[p][q] = len; // p指向q 106 c[q][p] = len; // q指向p,這樣表示無向圖 107 } 108 } 109 110 for(int i=1; i<=n; ++i) 111 dist[i] = maxint; 112 for(int i=1; i<=n; ++i) 113 { 114 for(int j=1; j<=n; ++j) 115 printf("%8d", c[i][j]); 116 printf("\n"); 117 } 118 119 Dijkstra(n, 1, dist, prev, c); 120 121 // 最短路徑長度 122 cout << "源點到最后一個頂點的最短路徑長度: " << dist[n] << endl; 123 124 // 路徑 125 cout << "源點到最后一個頂點的路徑為: "; 126 searchPath(prev, 1, n); 127 }
輸入數據:
5
7
1 2 10
1 4 30
1 5 100
2 3 50
3 5 10
4 3 20
4 5 60
輸出數據:
999999 10 999999 30 100
10 999999 50 999999 999999
999999 50 999999 20 10
30 999999 20 999999 60
100 999999 10 60 999999
源點到最后一個頂點的最短路徑長度: 60
源點到最后一個頂點的路徑為: 1 -> 4 -> 3 -> 5
最后給出兩道題目練手,都是直接套用模版就OK的:
1.HDOJ 1874 暢通工程續
http://www.wutianqi.com/?p=1894
2.HDOJ 2544 最短路
http://www.wutianqi.com/?p=1892
模板2:
1 /*Dijkstra求單源最短路徑 2010.8.26*/ 2 3 #include <iostream> 4 #include<stack> 5 #define M 100 6 #define N 100 7 using namespace std; 8 9 typedef struct node 10 { 11 int matrix[N][M]; //鄰接矩陣 12 int n; //頂點數 13 int e; //邊數 14 }MGraph; 15 16 void DijkstraPath(MGraph g,int *dist,int *path,int v0) //v0表示源頂點 17 { 18 int i,j,k; 19 bool *visited=(bool *)malloc(sizeof(bool)*g.n); 20 for(i=0;i<g.n;i++) //初始化 21 { 22 if(g.matrix[v0][i]>0&&i!=v0) 23 { 24 dist[i]=g.matrix[v0][i]; 25 path[i]=v0; //path記錄最短路徑上從v0到i的前一個頂點 26 } 27 else 28 { 29 dist[i]=INT_MAX; //若i不與v0直接相鄰,則權值置為無窮大 30 path[i]=-1; 31 } 32 visited[i]=false; 33 path[v0]=v0; 34 dist[v0]=0; 35 } 36 visited[v0]=true; 37 for(i=1;i<g.n;i++) //循環擴展n-1次 38 { 39 int min=INT_MAX; 40 int u; 41 for(j=0;j<g.n;j++) //尋找未被擴展的權值最小的頂點 42 { 43 if(visited[j]==false&&dist[j]<min) 44 { 45 min=dist[j]; 46 u=j; 47 } 48 } 49 visited[u]=true; 50 for(k=0;k<g.n;k++) //更新dist數組的值和路徑的值 51 { 52 if(visited[k]==false&&g.matrix[u][k]>0&&min+g.matrix[u][k]<dist[k]) 53 { 54 dist[k]=min+g.matrix[u][k]; 55 path[k]=u; 56 } 57 } 58 } 59 } 60 61 void showPath(int *path,int v,int v0) //打印最短路徑上的各個頂點 62 { 63 stack<int> s; 64 int u=v; 65 while(v!=v0) 66 { 67 s.push(v); 68 v=path[v]; 69 } 70 s.push(v); 71 while(!s.empty()) 72 { 73 cout<<s.top()<<" "; 74 s.pop(); 75 } 76 } 77 78 int main(int argc, char *argv[]) 79 { 80 int n,e; //表示輸入的頂點數和邊數 81 while(cin>>e>>n&&e!=0) 82 { 83 int i,j; 84 int s,t,w; //表示存在一條邊s->t,q權值為w 85 MGraph g; 86 int v0; 87 int *dist=(int *)malloc(sizeof(int)*n); 88 int *path=(int *)malloc(sizeof(int)*n); 89 for(i=0;i<N;i++) 90 for(j=0;j<M;j++) 91 g.matrix[i][j]=0; 92 g.n=n; 93 g.e=e; 94 for(i=0;i<e;i++) 95 { 96 cin>>s>>t>>w; 97 g.matrix[s][t]=w; 98 } 99 cin>>v0; //輸入源頂點 100 DijkstraPath(g,dist,path,v0); 101 for(i=0;i<n;i++) 102 { 103 if(i!=v0) 104 { 105 showPath(path,i,v0); 106 cout<<dist[i]<<endl; 107 } 108 } 109 } 110 return 0; 111 }