单源最短路(Dijkstra算法)
@Author: 张海拔
@Update: 2015-03-11
@Link: http://www.cnblogs.com/zhanghaiba/p/3514570.html
Dijkstra算法
总的来说:算法从开始节点,按照总路径权值非递减的顺序去搜索所有路径,直到发现指定的终止节点或发现全部节点为止。
——我们先看看算法的步骤:
算法是递推的
先设d[begin] = 0, d[others] = INF
for [0, n)
1、未标记的节点中,选出当前d值最小的 (最快logn效率)
2、标记节点i
3、据节点i,若d[i] + w[i][j] < d[j],则更新d[j](d[j]可能变小,O(n)效率)
——我们再看看为什么这样做
循环每一步能确定以i节点为终点的最短路径(定义这种节点为:“最短路终点”)
而且很关键的一点是,这个最短路径终点的d值是最小递增或者说一定是非递减的
也就是上面说的“按照总路径权值非递减的顺序去搜索所有路径”
另外,d值的意义是:当前状态下,以该节点为终点的最短路径长度
假定A是起点,显然是A为终点的最短路径,d值是0
后续步骤中都是从已确定最短路终点集合中,递推出下一个最短路终点
想想最简单的情况
A只能到B和C和D,AB边长5,AC边长3,AD边长4
当前最短路终点集合只有A,集合一步可达的节点有B C D
它们目前的d值分别是5 3 4
把最小d值的C纳入最短路终点集合,因为它的d值无法变得更小,也就是说从当前状态往后这个节点的d值都是确定的
为什么呢?
把C纳入最短路终点集合,若CB边长是1,则会修改B的d值变为4,所以非最小值d值的节点,其d值可能会变得更小
反过来,若纳入其它任何节点到最短路终点集合,都不可能使其d值变得更小
因为不会出现 dNonMin + edge < dMin 的情况
后续情况同理类推
所以原理就是:
根据已经最短路终点集合来递推下一个最短路终点
过程使最短路终点集合一步可达的那些节点的d值不断减小
直到某节点d值无法减小,则纳入最短路终点集合
代码实现:
1 /* 2 *Author: ZhangHaiba 3 *Date: 2014-1-10 4 *File: dijkstra.c 5 * 6 *dijkstra's algorithm demo 7 */ 8 9 #include <stdio.h> 10 #include <string.h> 11 #define N 512 12 #define INF 0x7fffffff; 13 14 void dijkstra(int, int); 15 void print_path(int, int); 16 void set_mat(int); 17 void show_mat(int); 18 19 int mat[N][N]; 20 int vis[N]; 21 int d[N]; 22 int par[N]; 23 24 int main(void) 25 { 26 int n; 27 28 scanf("%d", &n); 29 set_mat(n); 30 //according to the question descript, begin=0, end=1 31 int begin_v = 0, end_v = 1; 32 33 dijkstra(begin_v, n); 34 //print path: begin:0, end:par[1] 35 print_path(begin_v, par[end_v]); 36 printf("%d\n", end_v); 37 printf("%d\n", d[end_v]); 38 return 0; 39 } 40 41 void dijkstra(int begin, int n) 42 { 43 44 int t, i, min, x, y; 45 46 //init 47 memset(vis, 0, sizeof vis); 48 for (i = 0; i < n; ++i) 49 d[i] = INF; 50 d[begin] = 0; 51 par[begin] = begin; 52 53 //loop n times 54 for (t = 0; t < n; ++t) { 55 //choice min d[i] 56 min = INF; 57 for (i = 0; i < n; ++i) 58 if (!vis[i] && d[i] < min) 59 min = d[x = i]; 60 61 vis[x] = 1; 62 63 //update 64 for (y = 0; y < n; ++y) 65 if (mat[x][y] > 0 && d[y] > d[x] + mat[x][y]) { 66 d[y] = d[x] + mat[x][y]; 67 par[y] = x; 68 } 69 } 70 } 71 72 void print_path(int begin, int i) 73 { 74 if (i != begin) 75 print_path(begin, par[i]); 76 printf("%d ", i); 77 } 78 79 void set_mat(int n) 80 { 81 int i, j; 82 83 for (i = 0; i < n; ++i) 84 for (j = 0; j < n; ++j) 85 scanf("%d", &mat[i][j]); 86 } 87 88 void show_mat(int n) 89 { 90 int i, j; 91 92 for (i = 0; i < n; ++i) 93 for (j = 0; j < n; ++j) 94 printf(j == n-1 ? "%d\n" : "%d ", mat[i][j]); 95 }
测试用例
输入:
5
0 100 0 30 10
0 0 0 60 0
0 10 0 0 0
0 0 20 0 0
0 0 50 0 0
输出
0 3 2 1
60