最短路径
在一个无权的图中,若从一个顶点到另一个顶点存在着一条路径,则称该路径长度为该路径上所经过的边的数目,它等于该路径上的顶点数减1。由于从一个顶点到另一个顶点可能存在着多条路径,每条路径上所经过的边数可能不同,即路径长度不同,把路径长度最短(即经过的边数最少)的那条路径叫作最短路径或者最短距离。
对于带权的图,考虑路径上各边的权值,则通常把一条路径上所经边的权值之和定义为该路径的路径长度或带权路径长度。从源点到终点可能不止一条路径,把带权路径长度最短的那条路径称为最短路径,其路径长度(权值之和)称为最短路径长度或最短距离。
最短路径算法
Dijkstra算法 该算法是用于求解单源点最短路径的实用算法
Dijkstra算法的基本思想如下:
设置并逐步扩充一个集合S,存放已求出其最短路径的顶点,则尚未确定最短路径的顶点集合是V-S其中,V为网中所有顶点集合。按最短路径长度递增的顺序逐个用V-S中的顶点加到S中,直到S中包含全部顶点,而V-S为空。
Dijkstra算法的具体步骤;
(1)设源点为V,则S中只包含顶点V,令W=V-S,则W中包含除V外图中所有顶点。V对应的距离值为0,即D[1]=0。W中顶点对应的距离值是这样规定的:若图中有弧,则V顶点的距离为此弧权值,否则为(一个无穷大的数);
(2)从W中选择一个其距离值最小的顶点,并加入到S中;
(3)每往S中加入一个顶点后,就要对W中各个顶点的距离值进行一次修改。若加进做中间顶点,使+的值小于值,则用+代替原来的距离值;
(4)重复步骤2和3,即在修改过的W中的选距离值最小的顶点加入到S中,并修改W中的各个顶点的距离值,如此进行下去,知道S中包含图中所有顶点为之,即S=V。这是D[N]就是从到的最短路径长度值。
Floyd算法 该算法能够求得任意顶点之间的最短路径。
Floyd算法的基本思想是:
任意2个顶点到的距离的带权邻接矩阵开始,每次插入一个顶点,然后将到间的已知最短路径与插入顶点作为中间顶点时可能产生的到路径距离比较,取较小值以得到新的距离矩阵.如此循环迭代下去,依次构造出N个矩阵,,…,,当所有的顶点均作为任意 2个顶点到中间顶点时得到的最后的带权邻接矩阵就反映了所有顶点对之间的最短距离信息,成为图G的距离矩阵。
构造图G的距离矩阵:
设G是n个顶点的图,且其顶点用从1到n的整数编号。
(1)把G的带权邻接矩阵D作为距离矩阵的初值,即==D。当两点之间有边时, 等于边的权;当两点之间没有边时,记为无穷大;当i=j时,=0.
(2)构造=,其中=min(,+)是从到的只允许以作为中间点的路径中最短路的长度。
(3)继续构造,其中2≤k≤n,其中=min(,+)是从到的只允许以、、… 、作为中间点的路径中最短路的长度。
(4)这时图G的距离矩阵就构造出来了。
算法应用
假定现有一公交网络如图3.1所示。假设该网络中任意一对有直接通路的顶点间的通路都是双向可行的,则可以将其抽象为一个无向带权图,并且各相邻顶点间的直接距离如表3.1所示,现要求解的问题是:寻找从A点出发到达其他各顶点的最短路径。根据Dijkstra算法,可得出搜索过程和结果如表3.2所示,运用MATLAB编程的结果见附录。
图3.1 公交网络
从表3.2可以看出,从A点出发到达其它各顶点的最短路径,按递增顺序依次为AC(2km),AB(3km),ACD(4km),ACE(5km),ACDF(7km),ACEFG(11km)。
在实际应用中如果只是为了寻找两个指定顶点之间的最短路径,则可以给每个顶点赋予一个未访问标号(F)或已访问标号(T)。F表示从起始点到目的点之间还未找到最短路径,T表示从起始点到目的点之间已找到最短路径。这样每一次计算的目的是为了找到某个顶点将其F标号变成T标号,一旦目的顶点的标号变成T,则表示已寻找到从起始点到目的点之间的最短路径搜寻计算过程即可停止。例如,在上述实例中,如果只需求解从A点到达E点的最短路径,则搜寻会在A点、C点、B点、D点、E点的标号依次变成T之后结束,即 E点标号变成T后表示到达E点的最短路径已找到。
表3.1 无向图的边权列表
路段 | 距离(km) | 路段 | 距离(km) |
AB | 3 | CF | 7 |
AC | 2 | DE | 5 |
AD | 5 | DF | 6 |
BC | 2 | DG | 10 |
BE | 4 | EF | 2 |
BF | 5 | EG | 7 |
CD | 2 | FG | 4 |
CE | 3 |
|
|
表3.2 最短路径算法的搜索过程和结果
步骤 | 集合S | 源点到各个顶点的距离 | 最小距离值 | 待加入顶点 | ||||||
D[A] | D[B] | D[C] | D[D] | D[E] | D[F] | D[G] | ||||
1 | A | 0 | 3 | 2 | 5 | 无穷大 | 无穷大 | 无穷大 | 2 | C |
2 | A,C | 0 | 3 | 2 | 2+2=4 | 2+3=5 | 2+7=9 | 无穷大 | 3 | B |
3 | A,C,B | 0 | 3 | 2 | 4 | 5 | 3+5=8 | 无穷大 | 4 | D |
4 | A,C,B,D | 0 | 3 | 2 | 4 | 5 | 8 | 4+10=14 | 5 | E |
5 | A,C,B,D,E | 0 | 3 | 2 | 4 | 5 | 5+2=7 | 5+7=12 | 7 | F |
6 | A,C,B,D,E,F | 0 | 3 | 2 | 4 | 5 | 7 | 7+4=11 | 11 | G |
7 | A,C,B,D,E,F,G | 0 | 3 | 2 | 4 | 5 | 7 | 11 |
|
|
Floyd算法执行过程:
例如对于以下的一个有向图,对应的临街矩阵的形式为:
邻接矩阵:
第一步:以定点0作为松弛的点,考虑a[i][j]表示定点i到顶点j经由顶点0的最短路径长度,经过比较,没有任何路径得到修改,因此有:
第二步:以定点1作为松弛的点,考虑a[i][j]表示定点i到顶点j经由顶点1的最短路径长度,经过比较,顶点0到顶点1由原来的没有路径变为0—1—2的路径,其长度为9;因此有:
第三步:以定点2作为松弛的点,考虑a[i][j]表示定点i到顶点j经由顶点2的最短路径长度,经过比较,顶点1到顶点0由原来的没有路径变为1—2—0的路径,其长度为7;
顶点3到顶点0由原来的没有路径变为3—2—0的路径,其长度为4
顶点3到顶点3由原来的没有路径变为3—2—1的路径,其长度为4因此有:
第四步:以定点3作为松弛的点,考虑a[i][j]表示定点i到顶点j经由顶点3的最短路径长度,经过比较,顶点0到顶点2由原来的路径长度为9,路径为 0—1—2,变为0—3—2,其长度为8;
顶点1到顶点0由原来的路径长度为7,路径为1—2—0,变为1—3—2—0,其长度为6;
顶点1到顶点2由原来的路径长度为4,路径为1—2 ,变为1—3—2 ,其长度为3;
如果设n为图中顶点的个数,则弗洛伊德的代码为:
void Floyd()
{
for(int k=0; k<n; k++)
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
{
if(Tu[i][j]>Tu[i][k]+Tu[k][j])
Tu[i][j]=Tu[i][k]+Tu[k][j];
}
}