用 Dijkstra 算法也可以求得有向图 G=(V,E) 中每一对顶点间的最短路径。方法是:每次以一个不同的顶点为源点重复 Dijkstra 算法便可求得每一对顶点间的最短路径,时间复杂度是 O(n3) 。
弗罗伊德(Floyd)提出了另一个算法,其时间复杂度仍是O(n3) , 但算法形式更为简明,步骤更为简单,数据结构仍然是基于图的邻接矩阵。
算法思想
设顶点集S(初值为空),用数组 A 的每个元素 A[i][j] 保存从 Vi 只经过S中的顶点到达 Vj 的最短路径长度,其思想是:
① 初始时令S={ } , A[i][j] 的赋初值方式是:
② 将图中一个顶点 Vk 加入到S中,修改 A[i][j] 的值,修改方法是:
原因: 从Vj只经过S中的顶点(Vk)到达 Vj 的路径长度可能比原来不经过 Vk 的路径更短。
③ 重复②,直到G的所有顶点都加入到S中为止。
算法实现
◆ 定义二维数组 Path[n][n](n为图的顶点数) ,元素 Path[i][j] 保存从 Vi 到 Vj 的最短路径所经过的顶点。
◆ 若 Path[i][j]=k:从 Vi 到 Vj 经过 Vk ,最短路径序列是 (Vi , …, Vk , …, Vj) ,则路径子序列:(Vi , …, Vk) 和 (Vk , …, Vj) 一定是从 Vi 到 Vk 和从 Vk 到 Vj 的最短路径。从而可以根据 Path[i][k] 和 Path[k][j] 的值再找到该路径上所经过的其它顶点,…依此类推。
◆ 初始化为 Path[i][j] = -1,表示从 Vi 到 Vj 不经过任何(S中的中间)顶点。当某个顶点 Vk 加入到 S 中后使 A[i][j] 变小时,令Path[i][j]=k。
下面给出了利用Floyd算法求带权有向图的任意一对顶点间最短路径的过程。
根据上述过程中 Path[i][j] q数组,得出:
- V0到V1 :最短路径是{ 0, 1 } ,路径长度是2 ;
- V0到V2 :最短路径是{ 0, 1, 2 } ,路径长度是6 ;
- V1到V0 :最短路径是{ 1, 2, 0 } ,路径长度是9 ;
- V1到V2 :最短路径是{ 1, 2 } ,路径长度是4 ;
- V2到V0 :最短路径是{ 2, 0 } ,路径长度是5 ;
- V2到V1 :最短路径是{ 2, 0, 1 } ,路径长度是7 ;
算法实现
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */
#include<malloc.h> /* malloc()等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include<process.h> /* exit() */
#include<limits.h> //常量INT_MAX和INT_MIN分别表示最大、最小整数
/* 函数结果状态代码 */
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
#define MAX_NAME 5 /* 顶点字符串的最大长度+1 */
#define MAX_INFO 20 /* 相关信息字符串的最大长度+1 */
typedef int VRType;
typedef char VertexType[MAX_NAME];
typedef char InfoType;
/* --------------------------------- 图的数组(邻接矩阵)存储表示 --------------------------------*/
#define INFINITY INT_MAX /* 用整型最大值代替∞ */
#define MAX_VERTEX_NUM 20 /* 最大顶点个数 */
typedef enum { DG, DN, AG, AN }GraphKind; /* {有向图,有向网,无向图,无向网} */
typedef struct
{
VRType adj; /* 顶点关系类型。对无权图,用1(是)或0(否)表示相邻否; */
/* 对带权图,c则为权值类型 */
InfoType *info; /* 该弧相关信息的指针(可无) */
}ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedef struct
{
VertexType vexs[MAX_VERTEX_NUM]; /* 顶点向量 */
AdjMatrix arcs; /* 邻接矩阵 */
int vexnum, arcnum; /* 图的当前顶点数和弧数 */
GraphKind kind; /* 图的种类标志 */
}MGraph;
/* ---------------------------------------------------------------------------------------------*/
typedef int PathMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedef int ShortPathTable[MAX_VERTEX_NUM];
/* --------------------------- 需要用的图的数组(邻接矩阵)存储的基本操作 --------------------------*/
int LocateVex(MGraph G, VertexType u)
{ /* 初始条件:图G存在,u和G中顶点有相同特征 */
/* 操作结果:若G中存在顶点u,则返回该顶点在图中位置;否则返回-1 */
int i;
for (i = 0; i < G.vexnum; ++i)
if (strcmp(u, G.vexs[i]) == 0)
return i;
return -1;
}
Status CreateDN(MGraph *G)
{ /* 采用数组(邻接矩阵)表示法,构造有向网G */
int i, j, k, w, IncInfo;
char s[MAX_INFO], *info;
VertexType va, vb;
printf("请输入有向网G的顶点数,弧数,弧是否含其它信息(是:1,否:0): ");
scanf("%d,%d,%d", &(*G).vexnum, &(*G).arcnum, &IncInfo);
printf("请输入%d个顶点的值(<%d个字符):\n", (*G).vexnum, MAX_NAME);
for (i = 0; i < (*G).vexnum; ++i) /* 构造顶点向量 */
scanf("%s", (*G).vexs[i]);
for (i = 0; i < (*G).vexnum; ++i) /* 初始化邻接矩阵 */
for (j = 0; j < (*G).vexnum; ++j)
{
(*G).arcs[i][j].adj = INFINITY; /* 网 */
(*G).arcs[i][j].info = NULL;
}
printf("请输入%d条弧的弧尾 弧头 权值(以空格作为间隔): \n", (*G).arcnum);
for (k = 0; k < (*G).arcnum; ++k)
{
scanf("%s%s%d%*c", va, vb, &w); /* %*c吃掉回车符 */
i = LocateVex(*G, va);
j = LocateVex(*G, vb);
(*G).arcs[i][j].adj = w; /* 有向网 */
if (IncInfo)
{
printf("请输入该弧的相关信息(<%d个字符): ", MAX_INFO);
gets(s);
w = strlen(s);
if (w)
{
info = (char*)malloc((w + 1) * sizeof(char));
strcpy(info, s);
(*G).arcs[i][j].info = info; /* 有向 */
}
}
}
(*G).kind = DN;
return OK;
}
/* --------------------------------------------------------------------------------------------------*/
/* 实现算法7.16的程序 */
//typedef int PathMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedef int DistancMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
void ShortestPath_FLOYD(MGraph G, int *P[MAX_VERTEX_NUM][MAX_VERTEX_NUM][MAX_VERTEX_NUM], DistancMatrix *D)
{ /* 用Floyd算法求有向网G中各对顶点v和w之间的最短路径P[v][w]及其 */
/* 带权长度D[v][w]。若P[v][w][u]为TRUE,则u是从v到w当前求得最短 */
/* 路径上的顶点。算法7.16 */
int u, v, w, i;
for (v = 0; v < G.vexnum; v++) /* 各对结点之间初始已知路径及距离 */
for (w = 0; w < G.vexnum; w++)
{
(*D)[v][w] = G.arcs[v][w].adj;
for (u = 0; u < G.vexnum; u++)
(*P)[v][w][u] = FALSE;
if ((*D)[v][w] < INFINITY) /* 从v到w有直接路径 */
{
(*P)[v][w][v] = TRUE;
(*P)[v][w][w] = TRUE;
}
}
for (u = 0; u < G.vexnum; u++)
for (v = 0; v < G.vexnum; v++)
for (w = 0; w < G.vexnum; w++)
if ((*D)[v][u] + (*D)[u][w] < (*D)[v][w]) /* 从v经u到w的一条路径更短 */
{
(*D)[v][w] = (*D)[v][u] + (*D)[u][w];
for (i = 0; i < G.vexnum; i++)
(*P)[v][w][i] = (*P)[v][u][i] || (*P)[u][w][i];
}
}
void main()
{
MGraph g;
int i, j, k, l, m, n;
//PathMatrix p;
int p[MAX_VERTEX_NUM][MAX_VERTEX_NUM][MAX_VERTEX_NUM];
DistancMatrix d;
CreateDN(&g);
for (i = 0; i < g.vexnum; i++)
g.arcs[i][i].adj = 0; /* ShortestPath_FLOYD()要求对角元素值为0 */
printf("邻接矩阵:\n");
for (i = 0; i < g.vexnum; i++)
{
for (j = 0; j < g.vexnum; j++)
printf("%11d", g.arcs[i][j]);
printf("\n");
}
ShortestPath_FLOYD(g, &p, &d);
printf("d矩阵:\n");
for (i = 0; i < g.vexnum; i++)
{
for (j = 0; j < g.vexnum; j++)
printf("%6d", d[i][j]);
printf("\n");
}
for (i = 0; i < g.vexnum; i++)
for (j = 0; j < g.vexnum; j++)
printf("%s到%s的最短距离为%d\n", g.vexs[i], g.vexs[j], d[i][j]);
printf("p矩阵:\n");
l = strlen(g.vexs[0]); /* 顶点向量字符串的长度 */
for (i = 0; i < g.vexnum; i++)
{
for (j = 0; j < g.vexnum; j++)
{
if (i != j)
{
m = 0; /* 占位空格 */
for (k = 0; k < g.vexnum; k++)
if (p[i][j][k] == 1)
printf("%s", g.vexs[k]);
else
m++;
for (n = 0; n < m*l; n++) /* 输出占位空格 */
printf(" ");
}
else
for (k = 0; k < g.vexnum*l; k++) /* 输出占位空格 */
printf(" ");
printf(" "); /* 输出矩阵元素之间的间距 */
}
printf("\n");
}
}