最短路径
1.最短路径问题
一般是求带权有向图G = (V,E)中的一个或多个点到G中其他各个点的最短路径。
源点:路径上的第一个顶点
终点:最后一个顶点
求解方法:
- 单源点最短路径——迪杰斯特拉(Dijkstra)算法
- 多源点最短路径——佛洛伊德(Floyd)算法
2.Dijkstra算法
——从一个指定给源点到图上其余各点的最短路径
【思路】按路径长度递增的次序产生最短路径
-
引进一个辅助向量D,每个分量D[i]表示当前所找到的起始点v到每个终点vi的最短路径的长度。
-
(类比于最小生成树中的closedge数组的使用,每次纳入一个点都更新一次数组,只不过closedge是整个连通分量到其他顶点最短距离,现在是起点V₀到其他各个顶点的最短距离)
- 初始状态:D[ 0 ] = arcs[ v ][ vi ],S = { v }
- 选取当前最短路径:D[ j ] = Min { D[ i ] | vi ∈ V-S},vj就是当前求得的一条从v0出发的最短路径的终点,令S = S U { vj }
- 更新从v到V-S上任一顶点vk的当前最短路径长度: D[ k ] = Min { D[ k ],D[ j ] + arcs[ vj ][ vk ] }
- 重复执行2、3共n-1次
算法满足最优子结构原则
图示如下:
该思想可以表示成下列伪代码:
清除所有点的标号 //这些点的标号使用来表示两点间的距离是否已经算过,
//如果标记了就说明算过了,没有标记就要重新计算
//这样一来可以避免重复计算,提高效率
设d[0] = 0,其他d[i] = INF;
循环n次{
在所有未标号结点中,选出d值最小的结点x;
给结点x标记;
对于从x出发的所有边(x, y),更新d[y] = MIN{d[y], d[x]+w[x,y]}; //w[x,y]的值点x和y之间的权值
}
代码实现如下:
(图的实现形式:二位数组)
void ShortestPath_DIJ(MGraph G, int v0, PathMartrix &P, int &D){
int i, v, w;
Status final[MAX_VERTEX_NUM];//final[v]为TRUE当且仅当v∈S,即已经球的从v0到v的最短距离
//1.初始化,把任意两点间距离距离都算作无穷大,而且现在done全部为FALSE;
//2.设定初始状态
for(v = 0;v < G.vexnum; v++){
final[v] = FALSE; //表示v0和vi点间的最短距离并没有找到(vi中包括v0)
distance[v] = G.arcs[v0][v].adj; //各点和v0的距离放入时时变化的distance数组中
distance[v0] = 0;final[v0] = TRUE; //还要考虑边界因素,从v0到v0,distance[v0] = 0,也就是相当于找到了从v0到v0的最短距离了,所以final[v0] = 0;
//开始主循环,每次求得v0到vi的最短距离,就把vi并入S集合中(初始状态时,S = {v0})
for(i = 1; i < G.vexnum; i++){
int min = INFINITY; //min表示当前所知离v0顶点的最近距离(现在是对v0的初始化)
for(w = 0; w < G.vexnum; ++w){
if(!final[w]) //v0到w点的最短距离没找到,即点w不在S集合中
if(distance[w] < min){v = w; min = distance[w];}//说明w里v0点更近
}
final[v] = TRUE; //循环一边之后,就找到了min距离对应的点
//选完点后修改distance数组
for(w = 0; w < G.vexnum; w++){
if(!final[w]&&((min+G.arcs[v][w].adj)<distance[w])){//看是原来的小还是纳入点后新的通路距离小,如果新点与其他点没有路径,就是INFINITY
distance[w] = min + G.arcs[v][w].adj;
}
}
}
}
}
3.Floyd算法
typedef int PathMartrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedef int ShortPathTable[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
void ShortestPath_FLOYD(MGraph G){
int v, w, k;
PathMartrix p; //二维数组。用来描绘路径,比如
ShortPathTable D; //二维数组,表示出了所有边的大小(所两点之间不存在边,就把边的weight设置成infinty)
//初始化:
for(v = 0; v < G.vexnum; v++){
for(w = 0; w < G.vexnum; v++){
D[v][w] = G.arcs[v][w].adj;
p[v][w] = w;
}
}
//算法思想:两点之间的原来距离会不会因为加入一个点而减少,如果会,就改变两点之间的最小距离,而且加入的点可以不止一个,可以有n个(包括那两个点本身也是可以的)
for(k = 0; k < G.vexnum; k++){ //假设点k为v、w两点之间的插入点
for(v = 0; v < G.vexnum; v++){
for(w = 0;w < G.vexnum; w++){
if(D[v][w] > D[v][k] + D[k][w]){
D[v][w] = D[v][k] + D[k][w]; //若原先的距离大于插入点后的距离,就改变最小距离的值
p[v][w] = p[v][k]; //
}
}
}
}
//路径打印
for(v = 0; v < G.vexnum; v++){
for(w = v+1; w < G.vexnum; w++){
printf("\nV%d-->V%d weight:%d\n", v, w, D[v][w]);
k = p[v][w];
printf("Path:V%d", v);
while(k != w){
printf("-->V%d", k);
k = p[k][w];
}
printf("-->%d", w);
}
}
}