最短路径问题

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思想

D_{{i,j,k}}为从ij的只以(1..k)集合中的节点为中间节点的最短路径的长度。

  1. 若最短路径经过点k,则D_{{i,j,k}}=D_{{i,k,k-1}}+D_{{k,j,k-1}}
  2. 若最短路径不经过点k,则D_{{i,j,k}}=D_{{i,j,k-1}}

因此,D_{{i,j,k}}={\mbox{min}}(D_{{i,j,k-1}},D_{{i,k,k-1}}+D_{{k,j,k-1}})

 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)

 

posted @ 2017-05-06 10:44  whileskies  阅读(322)  评论(0编辑  收藏  举报