如果图是赋权图,那么问题就变得更困难了不过我们仍然可以使用来自无权情形时的想法。
我们保留所有与前面相同的信息。因此,每个顶点或者标记为Known的,或者标记为Unknown的。像之前一样,对每一个顶点保留一个临时距离dv。这个距离实际上是使用已知顶点作为中间顶点从s到v的最短路径的长。以前一样,我们记录pv,它是引起dv变化的最后的顶点。
解决单源最短路径问题的一般方法叫做Dijkstra算法。这个有30年历史的解法是贪婪算法(greddy algorithm)最好的例子。
Dijkstra像无权最短路径一样,按阶段进行。在每个阶段,Dijkstra算法选择一个顶点v,它在所有未知顶点中具有最小的dv,同时算法声明从s到v的最短路径是已知的。阶段的其余部分由dw的值更新完成。
在无权的情况下,若dw = x则置dw = dv+1,因此,若顶点v能提供一条更短路径,则我们本质上降低了dw的值。如果我们对赋权的情形应用同样的逻辑,那么当dw的新值dv+Cv,w是一个改进的值时,我们就置dw = dv + Cv,w。
简单来说,使用通向w路径上的定点v是不是一个好主意由算法决定。
有向图G
刚开始假设节点s是v1。第一个选择的定点是v1,路径的长为0。
该顶点标记为已知。既然v1已知,那么表项就需要调整。邻接到v1的顶点是v2和v4。这两个顶点的项得到调整。
下一步选取v4并标记为已知。顶点v3,v5,v6,v7是邻接的顶点,而它们实际上都需要调整。
接着选择v2。v4邻接的点,但已经是已知的,因此对它没有工作要做。v5是邻接的点,但不做调整,因为经过v2的值是2+10=12,而长为3的路径已经是已知的了。
下一个选取的顶点是v5,其值为3。v7是唯一的邻接顶点,但是它不用做调整,因此3+6>5。
然后选取v3,对v6的距离下调为3+5=8。
在选取下一个顶点v7。v6下调到5+1=6。
最后我们选择v6。
下面给出Dijkstra算法的为代码。这里我们假设,这些顶点从0到NumVertex-1标号,并通过ReadGraph我们的图可以被读入到一个邻接表中。
/*Dijkstra算法的声明*/typedef int Vertex;struct TableEntry{ List Header; /*adjacency list*/ int Known; DistType Dist; Vertex Path;}/*Vertices are numbered from 0*/#define NotAVertex -1typedef struct TableEntry Table[NumVertex];
/*表初始化*/void InitTable(Vertex start, Graph G, Table T){ int i; ReadGraph(G, T); for(i = 0; i < NumVertex; ++i){ T[i].Known = False; T[i].Dist = Infinity; T[i].Path = NotAVertex; } T[start].Dist = 0;}
/*显示实际最短路径的例程*/void PrintPath(Vertex V, Table T){ if(T[V].Path != NotAVertex){ PrintPath(T[V].PATH, T); printf(" to"); } printf("%v", V); /*%v is pseudocode*/}
/*Dijkstra算法的伪代码*/void Dijkstra(Table T){ Vertex V, W; for(;;){ V = smallest unknown distance vertex; if(V == NotAVertex) break; T[V].Known = True; for each W adjancent to V if(!T[W].known){ if(T[V].Dist + Cvw < T[W].Dist){ /*update W*/ Decreae(T[W].Dist to T[V].Dist + Cvw); T[W].Path = V; } } }}