弗罗伊德(Floyd)算法(第七章 P191 算法7.16)

用 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");
	}
}

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值