Dijkstra算法:有权图的单源最短路
1.最短路必定只经过S中的顶点
如果还存在一个w在S之外,v0>w必定小于v0>v,但路径是按照递增顺序生成的,那么w一定已经收录了,与前提矛盾。
2.新收录一个v,会影响v的邻接点的dist值
如果收录v使得s>w的路径变短,则s>w的路径一定经过v,并且v>w有一条边。如果v>w中间还存在顶点的话,该顶点一定在S之外(S是按递增顺序生成的,v刚被收录)而w的dist值为只经过S中的顶点。 dist[w] = min{dist[w], dist[v]+ <v, w>的权重}
3.过程
1.从V-S中找到dist[i]最小的下标i,收录到S中
2.更新与i的邻接点的dist值
3.V==S时停止
4.代码
1 //邻接矩阵存储 2 #define MaxVertexNum 1100 3 struct GraphNode 4 { 5 int Nv; 6 int Ne; 7 int G[MaxVertexNum][MaxVertexNum]; 8 }; 9 typedef struct GraphNode *MGraph; 10 11 Vertex FindMinDist(MGraph Graph, int dist[], int collected[]) 12 { 13 Vertex MinV, V; 14 int MinDist = INFINITY; 15 16 for (V = 0; V < Graph->Nv; V++) { 17 if (collected[V] == false && dist[V]<MinDist) //在未收录中找且dist[V]更小 18 { 19 MinDist = dist[V]; 20 MinV = V; 21 } 22 } 23 if (MinDist < INFINITY) 24 return MinV; 25 else 26 return ERROR; 27 } 28 29 bool Dijkstra(MGraph Graph, int dist[], int path[], Vertex s) 30 { 31 int collected[MaxVertexNum]; 32 Vertex v, w; 33 34 for (v = 0; v < Graph->Nv; v++) { 35 dist[v] = Graph->G[s][v]; 36 if (dist[v] < INFINITY) 37 path[v] = s; 38 else 39 path[v] = -1; 40 collected[v] = false; 41 } 42 dist[v] = 0; 43 collected[s] = true; 44 45 while (1) { 46 v = FindMinDist(Graph, dist, collected); 47 if (v == ERROR) 48 break; //全收录完 49 collected[v] = true; 50 for (w = 0; w < Graph->Nv; w++) { 51 if (collected[w] == false && Graph->G[v][w]<INFINITY) { 52 if (Graph->G[v][w] < 0) //负值圈 53 return false; 54 if (dist[v]+Graph->G[v][w] < dist[w]) { 55 dist[w] = dist[v] + Graph->G[v][w]; 56 path[w] = v; 57 } 58 } 59 } 60 } 61 return true; 62 }
5.时间复杂度
直接扫描所有未收录的顶点 T = O(|V|^2 + |E|) 稠密图效果好
若用最小堆存取dist 读取最小的并更新堆O(log|V|), 但更新dist[w]的值需要插入最小堆O(log|V|) T = O(|V|log|V| + |E|log|V|) = O(|E|log|V|) 稀疏图较好 (E和V同一个数量级,|V|log|V|比|V|^2要好)
若将单源最短路算法调用|V|遍,对于稠密图T = O(|V|^3 + |E||V|) 对于稀疏图较好
对于稠密图有Floyd算法
Floyd算法:多源最短路算法
采用DP思想
设为从到的只以集合中的节点为中间节点的最短路径的长度。
- 若最短路径经过点k,则;
- 若最短路径不经过点k,则。
因此,。
1 //邻接矩阵存储 2 3 bool Floyd(MGraph Graph, WeightType D[][MaxVertexNum], Vertex path[]) 4 { 5 Vertex i, j, k; 6 7 for (i = 0; i < Graph->Nv; i++) 8 for (j = 0; j < Graph->Nv; j++) { 9 D[i][j] = Graph->G[i][j]; 10 path[i][j] = -1; 11 } 12 13 for (k = 0; k < Graph->Nv; k++) 14 for (i = 0; i < Graph->Nv; i++) 15 for (j = 0; j < Graph->Nv; j++) 16 if (D[i][k] + D[k][j] < D[i][j]) 17 { 18 D[i][j] = D[i][k] + D[k][j]; 19 if (i == j && D[i][j] < 0) 20 return false; 21 path[i][j] = k; 22 } 23 return true; 24 }
输出路径:
递归输出, 先输出i到k,再输出k,再输出k到j路线。
时间复杂度
T = O(|V|^3)