图的最短路径
迪杰斯特拉(Dijkstra)算法
- 迪杰斯特拉算法是计算无向图或有向图的最短路径,而且是运用了深度遍历的方法来计算的。
- 其中数组 Patharc[MAXVEX] 用来存储最短路径中每个顶点的下标
- ShortPathTable[MAXVEX] 用来存放起始顶点到各顶点最短路径的权值和
- Final[k] 用来标记顶点 k 存在于最短路径的顶点集中
进行简单的演示
- 先来张无向图
- 以 V0 为起点开始构建最短路径之旅,那么 V0 已经称为最短路径顶点中的一员,在 Final[0] = 1 之前,要初始化起始顶点到各顶点的最短路径的权值和。直接上图。
- 首先遍历一遍存放最短路径的权值和的数组 ShortPathTable[] ,在其中找到最小的值及最小值对应的顶点,再以该顶点进行广度遍历,寻找到最短路径的延伸版
坐标变化如下
图形变化
找到最短路径为 V0 -> V1 -> V3 -> V2
- 还是和上面一样先遍历在进行类似的广度遍历
坐标变化如下
图形变化
找到最短路径为 V0 -> V1 -> V3 -> V2 -> V4
- So next like 上面一样先遍历在进行类似的广度遍历
坐标变化如下
图形变化
找到最短路径为 V0 -> V1 -> V3 -> V2 -> V4 -> V5
- 经过上面的步骤,就找到了最短步骤,数组 ShortPathTable[] 是存放起始顶点到各顶点的最短路径,数组 Patharc[] 是存放的是顶点是能和此时数组的索引构成边的。
接下来就上代码了
void ShortestPath_DIjkstra(MGraph G, int v0, int P[], int D[])
{
int k, Min;
int Final[MAXVEX]; //标记动态顶点存在最短路径
for (int v = 0; v < G.numVertexes; v++) //初始化数据 initialization data
{
Final[v] = 0; //初始化时标记各个顶点都未计入最短路径
D[v] = G.arc[v0][v]; //将边的权值存入数组中
P[v] = 0; //初始化路径数组都为0
}
D[v0] = 0; //起始顶点v0标记为0也就是V0到V0的边
Final[v0] = 1; //此处标记的是V0到V0为权值,也就是无路径
for (int v = 1; v < G.numVertexes; v++)
{
Min = INFINITY; //标记最小值并初始化
for (int w = 0; w < G.numVertexes; w++)
{
if (!Final[w] && D[w] < Min)
{
k = w; //k表示找到了最短路径的另一个顶点
Min = D[w];
}
}
Final[k] = 1; //标记该顶点已经纳入最短路径顶点群中一员
for (int w = 0; w < G.numVertexes; w++)
{
if (!Final[w] && Min + G.arc[k][w] < D[w])
{
D[w] = Min + G.arc[k][w];
P[w] = k;
}
}
}
}
int mian{
....
//迪杰斯特拉(Dijkstra)算法
int Patharc[MAXVEX]; //存放最短路径下标的数组
int ShortPathTable[MAXVEX]; //存储到各点最短路径和
int v0 = 0; //最短路径的起始顶点
ShortestPath_DIjkstra(G, v0, Patharc, ShortPathTable);
for (int i = 0; i < G.numVertexes; i++)
{
printf("%d ", ShortPathTable[i]);
}
.......
}
代码中创建图在,前面的记录中
该算法的时间复杂度是 O(n^2)
费罗伊德(Floyd)算法
Floyd 算法也是求最短路径,不过它更复杂一些,它将使用一位数组转向了使用二维数组,该算法使用了两个数组分别来存放顶点到顶点的最短路径权值和(代号A数组)以及顶点最小路径的中每个顶点的前驱前驱顶点(代号B数组)。
先来分析下 A 数组,根据下图来分析;在无向图中,我们会先对 A 数组进行初始化,也就是将图用矩阵来存储,初始化的矩阵存放的都是相邻两顶点之间边的权值,无穷大表示不构成边;现在要存放 V0 -> V3 这条路径的权值,但是邻接矩阵里面存放的是 [V0][V1] [V1][V3] [V0][V2] [V2][V3] 这些边的权值,但是要 存放路径 [V0][V3] 的权值只能这样 [V0][V1] + [V1][V3] 或者 [V0][V2] + [V2][V3],我们发现构建这条路径的方法很多,但是我们要根据 MIN{ [v][w], [v][k] + [k][w]} 这样的公式将构建 [v0][v3] 的最短路径权值存放进 A数组,由于里面已经存放了有一个中间顶点的路径,在这基础上可以计算并存放存在多个中间顶点的路径的权值和。
在分析 A 数组的同时,B 数组也要分析,首先 B 数组初始化后里面都是存放的(从左向右看)能构成边的两个顶点的右边顶点,然而是两个以上的顶点的路径呢(对应的数组里面存放的是前驱顶点),比如上面的 [V0][V3] ,在 B 数组中该位置就要存放最短路径的中间顶点 V1 了;复杂一点如 [V0][V4] 呢,此时 B 数组里面的值已经变换,先打印其实点 V0, 再打印路径中的这些顶点 [V0][V4] -> [ [V0][V4] ][4] -> [ [ [V0][V4] ][4] ][4] , 原理就是这些路径的顶点都是间接存放在相应的数组的列中(比如[V0][V4]存放在第四列中),条件是根据起始顶点来打印。
代码
void ShortestPath_Floyd(MGraph G, int (*P)[MAXVEX], int (*D)[MAXVEX])
{
int k;
//初始化路径矩阵和权值矩阵
for (int v = 0; v < G.numVertexes; v++)
{
for (int w = 0; w < G.numVertexes; w++)
{
P[v][w] = w;
D[v][w] = G.arc[v][w];
}
}
for (int k = 0; k < G.numVertexes; k++)
{
for (int v = 0; v < G.numVertexes; v++)
{
for (int w = 0; w < G.numVertexes; w++)
{
if (D[v][w] > D[v][k] + D[k][w])
{
D[v][w] = D[v][k] + D[k][w];
D[w][v] = D[v][w];
P[v][w] = P[v][k];
P[w][v] = P[v][w];
}
}
}
}
//求最短路径
for (int v = 0; v < G.numVertexes; v++)
{
for (int w = v + 1; w < G.numVertexes; w++)
{
printf("顶点start:%c --- 顶点end:%c weight:%d : ", G.vexs[v], G.vexs[w], D[v][w]);
k = P[v][w];
printf("The path is : %c", G.vexs[v]);
while(k != w)
{
printf(" -> %c", G.vexs[k]);
k = P[k][w];
}
printf(" -> %c\n", G.vexs[w]);
}
printf("\n");
}
}
int main(){
//弗洛伊德(Floyd)算法
int Pathmatrix[MAXVEX][MAXVEX];
int ShortPathTable[MAXVEX][MAXVEX];
ShortestPath_Floyd(G, Pathmatrix, ShortPathTable);
}
这个算法的复杂度就了,为 O(n^3)。