2021-12-3【数据结构/严蔚敏】【弗洛伊德(Floyd)】【代码实现算法7.16】

知识点

Floyd 算法
是用来求任意两个结点之间的最短路的;

复杂度比较高,但是常数小,容易实现。(我会说只有三个 for 吗?)

适用于任何图,不管有向无向,边权正负,但是最短路必须存在。(不能有个负环)

算法实现

我们定义一个数组 f[k][x][y],表示只允许经过结点 1 1 1 k k k(也就是说,在子图 V ′ = 1 , 2 , 3 , . . . , k V' = 1,2,3,...,k V=1,2,3,...,k 中的路径,注意, x x x y y y 不一定在这个子图中),结点 x x x 到结点 y y y 的最短路长度。

很显然,f[n][x][y] 就是结点 x x x 到结点 y y y 的最短路长度(因为 V ′ = 1 , 2 , 3 , . . . , n V' = 1,2,3,...,n V=1,2,3,...,n 即为 V V V 本身,其表示的最短路径就是所求路径)。

接下来考虑如何求出k数组的值。

f[0][x][y] x x x y y y 的边权,或者 0 0 0,或者 + ∞ +∞ +f[0][x][y] 什么时候应该是 + ∞ +∞ +?当 x x x y y y 间有直接相连的边的时候,为它们的边权;当 x = y x=y x=y 的时候为零,因为到本身的距离为零;当 x x x y y y 没有直接相连的边的时候,为 + ∞ +∞ +)。

f[k][x][y] = min(f[k-1][x][y], f[k-1][x][k]+f[k-1][k][y])f[k-1][x][y],为不经过 k k k 点的最短路径,而 f[k-1][x][k]+f[k-1][k][y],为经过了 k k k 点的最短路)。

上面两行都显然是对的,所以说这个做法空间是 O ( N 3 ) O(N^3) O(N3),我们需要依次增加问题规模( k k k 1 1 1 n n n),判断任意两点在当前问题规模下的最短路。

//算法7.16 
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){ 
	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) {
				(*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]) {
					(*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];
				}
}

完整代码

#include<bits/stdc++.h>
using namespace std; 
/* 函数结果状态代码 */
#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;
typedef int Status; 
typedef int Boolean;
/* ---------------------------------  图的数组(邻接矩阵)存储表示    --------------------------------*/
 
#define INFINITY INT_MAX // 用整型最大值代替∞ 
#define MAX_VERTEX_NUM 20 // 最大顶点个数 
typedef enum { DG, DN, AG, AN }GraphKind; // {有向图,有向网,无向图,无向网} 
typedef struct{
	VRType adj; 
	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){
	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 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){ 
	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) {
				(*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]) {
					(*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];
				}
}
 
int  main(){
	MGraph g;
	int i, j, k, l, m, n;
	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");
	}
}

测试样例

在这里插入图片描述

请输入有向网G的顶点数,弧数,弧是否含其它信息(:1,:0)(空格隔开): 3 5 0
请输入3个顶点的值(<5个字符):
v0 v1 v2
请输入5条弧的弧尾 弧头 权值(以空格作为间隔):
v0 v1 4
v0 v2 11
v2 v0 3
v1 v0 6
v1 v2 2

运行结果

邻接矩阵:
          0          4         11
          6          0          2
          3 2147483647          0
d矩阵:
     0     4     6
     5     0     2
     3     7     0
v0到v0的最短距离为0
v0到v1的最短距离为4
v0到v2的最短距离为6
v1到v0的最短距离为5
v1到v1的最短距离为0
v1到v2的最短距离为2
v2到v0的最短距离为3
v2到v1的最短距离为7
v2到v2的最短距离为0
p矩阵:
         v0v1     v0v1v2
v0v1v2            v1v2
v0v2     v0v1v2

更多数据结构代码实现请关注我的专栏数据结构

或者进入2021-10-16【严蔚敏数据结构代码实现合集】【c语言学习必备】学习

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
清华大学出版社出版的《数据结构(C语言版)》是清华大学计算机科学与技术系教材严蔚敏编写的经典教材。全书包含了数据结构的基本概念和常用算法的详细介绍,并提供了相应的代码实现。 《数据结构(C语言版)》的代码实现主要示例采用了C语言。全书共有12章内容,包括线性表、栈和队列、串、树与二叉树、图、查找、排序等。每一章节都配备了大量的算法示例和相应的C语言代码实现。下面以线性表为例简要介绍一下书中代码实现: 在第2章“线性表”的内容中,书中详细介绍了线性表的定义、基本操作以及线性表的顺序存储结构和链式存储结构的实现方法。 在顺序存储结构部分,书中给出了线性表的初始化、插入、删除、查找等基本操作的代码实现。例如,线性表的初始化操作可以通过以下C语言代码实现: ```c #define MAXSIZE 100 // 定义线性表的最大长度 typedef struct { int data[MAXSIZE]; // 存储数据元素的数组 int length; // 线性表的当前长度 } SqList; void InitList(SqList *L) { L->length = 0; // 初始化线性表长度为0 } ``` 在链式存储结构部分,书中介绍了线性表的链式存储结构以及常见的链表类型,如单链表、静态链表和循环链表。对于单链表,书中给出了插入、删除、查找等操作的代码实现。例如,线性表的插入操作可以通过以下C语言代码实现: ```c typedef struct Node { int data; // 数据域 struct Node *next; // 指针域 } Node; // 在第i个位置插入元素 int InsertList(Node *head, int i, int x) { Node *pre = head; // pre指向头结点 int j = 0; while (pre != NULL && j < i - 1) { // 遍历找到第i-1个结点 pre = pre->next; j++; } if (pre == NULL || j > i - 1) { return 0; // 位置错误返回0 } Node *new_node = (Node *)malloc(sizeof(Node)); new_node->data = x; new_node->next = pre->next; pre->next = new_node; return 1; } ``` 以上只是《数据结构(C语言版)》中部分代码实现的简单例子,全书还包含很多其他章节的代码实现。读者可以通过阅读这本教材更全面地了解数据结构的概念和常用算法,并借助书中提供的代码实现进行实际操作和学习。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eternity_GQM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值