最短路径算法Dijkstra algorithm
1 经典Dijkstra算法的主要思想
Dijkstra算法的基本思路是:假设每个点都有一对标号 (dj, pj),其中dj是从起源点s到点j的最短路径的长度 (从顶点到其本身的最短路径是零路(没有弧的路),其长度等于零);pj则是从s到j的最短路径中j点的前一点。求解从起源点s到点j的最短路径算法的基本过程如下:
1) 初始化。起源点设置为:① ds=0, ps为空;② 所有其他点: di=∞, pi=?;③ 标记起源点s,记k=s,其他所有点设为未标记的。
2) 检验从所有已标记的点k到其直接连接的未标记的点j的距离,并设置:dj=min[dj, dk+lkj],式中,lkj是从点k到j的直接连接距离。
3) 选取下一个点。从所有未标记的结点中,选取dj 中最小的一个i:di=min[dj, 所有未标记的点j],点i就被选为最短路径中的一点,并设为已标记的。
4) 找到点i的前一点。从已标记的点中找到直接连接到点i的点j*,作为前一点,设置:i=j*
5) 标记点i。如果所有点已标记,则算法完全推出,否则,记k=i,转到2) 再继续。
2 已有的Dijkstra算法的实现
从上面可以看出,在按标记法实现Dijkstra算法的过程中,核心步骤就是从未标记的点中选择一个权值最小的弧段,即上面所述算法的2)~5)步。 这是一个循环比较的过程,如果不采用任何技巧,未标记点将以无序的形式存放在一个链表或数组中。那么要选择一个权值最小的弧段就必须把所有的点都扫描一遍,在大数据量的情况下,这无疑是一个制约计算速度的瓶颈。要解决这个问题,最有效的做法就是将这些要扫描的点按其所在边的权值进行顺序排列,这样每循环 一次即可取到符合条件的点,可大大提高算法的执行效率。另外,GIS中的数据 (如道路、管网、线路等)要进行最短路径的计算,就必须首先将其按结点和边的关系抽象为图的结构,这在GIS中称为构建网络的拓扑关系 (由于这里的计算与面无关,所以拓扑关系中只记录了线与结点的关系而无线与面的关系,是不完备的拓扑关系)。如果用一个矩阵来表示这个网络,不但所需空间巨大,而且效率会很低。下面主要就如何用一个简洁高效的结构表示网的拓扑关系以及快速搜索技术的实现进行讨论。
网络在数学和计算机领域中被抽象为图,所以其基础是图的存储表示。一般而言,无向图可以用邻接矩阵和邻接多重表来表示,而有向图则可以用邻接表和十字链表表示,其优缺点的比较见表 1。
表 1 几种图的存储结构的比较
名 称 |
实现方法 |
优 点 |
缺 点 |
时间复杂度 |
邻接矩阵 |
二维数组 |
1. 易判断两点间的关系 |
占用空间大 |
O(n2+m*n) |
|
|
2. 容易求得顶点的度 |
|
|
邻接表 |
链表 |
1. 节省空间 |
1. 不易判断两点间的关系 |
O(n+m)或O(n*m) |
|
|
2. 易得到顶点的出度 |
2. 不易得到顶点的入度 |
|
十字链表 |
链表 |
1. 空间要求较小 |
结构较复杂 |
同邻接表 |
|
|
2.易求得顶点的出度和入度 |
|
|
邻接多重表 |
链表 |
1. 节省空间 |
结构较复杂 |
同邻接表 |
|
|
2. 易判断两点间的关系 |
|
|
3 Dijkstra图例
2 #define MAX_NODES 1024 /* maximum number of nodes */
3 #define INFINITY 1000000 /*a number larger than every maximum path,∞ */
4 int n; /* this graph has n nodes */
5 int dist[MAX_NODES][MAX_NODES]=/* dist[i][j] is the distance from to j */
6 {
7 {0,2,4,INFINITY,INFINITY,INFINITY},
8 {2,0,1,4,2,INFINITY},
9 {4,1,0,INFINITY,3,INFINITY},
10 {INFINITY,4,INFINITY,0,3,2},
11 {INFINITY,2,3,3,0,2},
12 {INFINITY,INFINITY,INFINITY,2,2,0}
13 };
14 enum Label{permanent,tentative};
15 /*
16 * This is Dijkstra algorithm
17 * @param s is the Destination node number
18 * @param t is the Start node number
19 * @param path[] is the shortest path
20 */
21 void shortest_path(int s,int t,int path[])
22 {
23 struct state /* the path being working on */
24 {
25 int predecessor; /* previous node */
26 int length;/*length from source to this node */
27 enum Label label; /* label state */
28 }state[MAX_NODES];
29 int i,k,min;
30 struct state *p;
31 for(p=&state[0];p<&state[n];p++) /* initialize state */
32 {
33 p->predecessor=-1;
34 p->length=INFINITY;
35 p->label=tentative;
36 }
37 state[t].length=0;
38 state[t].label=permanent;
39 k=t; /* k is the initial working node */
40 do /* Is there a better path from k? */
41 {
42 for(i=0;i<n;i++)/* this graph has n nodes */
43 {
44 if(dist[k][i]!=0 && state[i].label==tentative)
45 {
46 if(state[k].length+dist[k][i]<state[i].length)
47 {
48 state[i].predecessor=k;
49 state[i].length=state[k].length+dist[k][i];
50 }
51 }
52 }
53 /* Find the tentatively labeled node with the smallest label */
54 k=0;
55 min=INFINITY;
56 for(i=0;i<n;i++)
57 {
58 if(state[i].label==tentative && state[i].length<min)
59 {
60 min=state[i].length;
61 k=i;
62 }
63 }
64 state[k].label=permanent;
65 }while(k!=s);
66
67 /* Copy the path into the output array. */
68 i=0;
69 k=s;
70 do
71 {
72 path[i++]=k;
73 k=state[k].predecessor;
74 }while(k>=0);
75 }
76 void main()
77 {
78 n=6;
79 int path[6];
80 int i;
81 for(i=0;i<6;i++)
82 {
83 path[i]=-1;/* clear path[] */
84 }
85 shortest_path(6-1,0,path);
86 for(i=5;i>=0;i--)
87 {
88 printf("%d\n",path[i]);
89 }
90 }