图的Floyd算法——C/C++

本文深入讲解了Floyd算法,一种用于解决多源最短路径问题的经典算法。文章详细阐述了算法原理,包括如何通过邻接矩阵求解图中任意两点间最短路径,并介绍了算法的实现步骤和代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

例子:
在这里插入图片描述

一、算法原理

  1. 通过一个图的权值矩阵求出它的每两点间的最短路径矩阵。
  2. 从图的带权邻接矩阵A=[a(i,j)],n×n开始,递归地进行n次更新,即由矩阵D(0)=A,按一个公式,构造出矩阵D(1);又用同样地公式由D(1)构造出D(2);……;最后又用同样的公式由D(n-1)构造出矩阵D(n)。矩阵D(n)的i行j列元素便是i号顶点到j号顶点的最短路径长度,称D(n)为图的距离矩阵,同时还可引入一个后继节点矩阵path来记录两点间的最短路径。
  3. 采用松弛技术(松弛操作),对在i和j之间的所有其他点进行一次松弛。所以时间复杂度为O(n^3);

二、算法思想

1. 从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。
2. 对于每一对顶点 u 和v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比已知的路径更短。如果是更新它。
3. 把图用邻接矩阵G表示出来,如果从Vi到Vj有路可达,则G[i][j]=d,d表示该路的长度;否则G[i][j]=无穷大。定义一个矩阵D用来记录所插入点的信息,D[i][j]表示从Vi到Vj需要经过的点,初始化D[i][j]=j。把各个顶点插入图中,比较插点后的距离与原来的距离,G[i][j]= min( G[i][j], G[i][k]+G[k][j] ),如果G[i][j]的值变小,则D[i][j]=k。在G中包含有两点之间最短道路的信息,而在D中则包含了最短通路径的信息。
4. 比如,要寻找从V5到V1的路径。根据D,假如D(5,1)=3则说明从V5到V1经过V3,路径为{V5,V3,V1},如果D(5,3)=3,说明V5与V3直接相连,如果D(3,1)=1,说明V3与V1直接相连。

三、算法实现

1、Floyd算法

void Floyd(MatGraph MG) {
	int i, j, k;
	int A[MAXV][MAXV];
	int path[MAXV][MAXV];
	for (i = 0; i < MG.n; i++) {
		for (j = 0; j < MG.n; j++) {
			A[i][j] = MG.adjMat[i][j];
			if (i != j && MG.adjMat[i][j] < INF) {
				path[i][j] = i;							//顶点 i 到顶点 j 有边时
			}
			else {
				path[i][j] = -1;						//顶点 i 到顶点 j 无边时
			}
		}
	}
	for (k = 0; k < MG.n; k++) {						//一次考察所有顶点
		for (i = 0; i < MG.n; i++) {
			for (j = 0; j < MG.n; j++) {
				if (A[i][j] > A[i][k] + A[k][j]) {
					A[i][j] = A[i][k] + A[k][j];		//修改最短路径长度
					path[i][j] = path[k][j];			//修改最短路径
				}
			}
		}
	}
	displayPath(MG, A, path);							//输出最短路径
}

2、输出多源最短路径

void displayPath(MatGraph MG, int A[MAXV][MAXV], int path[MAXV][MAXV]) {
	int i, j, k;
	int s;
	int aPath[MAXV];										//存放一条最短路径(逆向)
	int d;													//顶点个数
	for (i = 0; i < MG.n; i++) {
		for (j = 0; j < MG.n; j++) {
			if (A[i][j] != INF && i != j) {					//若顶点 i 和 顶点 j 之间存在路径
				printf("从 %d 到 %d 的路径为:", i, j);
				k = path[i][j];
				d = 0;
				aPath[d] = j;								//路径上添加终点
				while (k != -1 && k != i) {					//路劲上添加中间点
					d++;
					aPath[d] = k;
					k = path[i][k];
				}
				d++;
				aPath[d] = i;								//路径上添加起点
				printf("%d", aPath[d]);						//输出起点
				for (s = d - 1; s >= 0; s--) {				//输出路径上其他顶点
					printf("->%d", aPath[s]);
				}
				printf("\t\t");
				printf("路径长度为:%d\n", A[i][j]);
			}
		}
	}
}

四、整体框架

#include<stdio.h>
#include<malloc.h>

#define MAXV 7					//最大顶点个数 
#define INF 32767				//定义 ∞
//∞ == 32767 ,int 型的最大范围(2位)= 2^(2*8-1),TC告诉我们int占用2个字节,而VC和LGCC告诉我们int占用4个字节
//图:Graph
//顶点:Vertex
//邻接:Adjacency
//矩阵:Matrix
//表:List
//边:Edge 

typedef struct vertex {
	int number;					//顶点的编号 	
}VertexType; 					//别名,顶点的类型 

typedef struct matrix {
	int n;						//顶点个数
	int e;						//边数 
	int adjMat[MAXV][MAXV];		//邻接矩阵数组			
	VertexType ver[MAXV];		//存放顶点信息 
}MatGraph;						//别名,完整的图邻接矩阵类型

typedef struct eNode {
	int adjVer;					//该边的邻接点编号 
	int weiLGht;				//该边的的信息,如权值 
	struct eNode* nextEdLGe;	//指向下一条边的指针 
}EdgeNode; 						//别名,边结点的类型 

typedef struct vNode {
	EdgeNode* firstEdLGe;		//指向第一个边结点 
}VNode; 						//别名,邻接表的头结点类型 

typedef struct list {
	int n;						//顶点个数
	int e;						//边数
	VNode adjList[MAXV];		//邻接表的头结点数组 
}ListGraph;						//别名,完整的图邻接表类型 

//创建图的邻接表 
void createAdjListGraph(ListGraph*& LG, int A[MAXV][MAXV], int n, int e) {
	int i, j;
	EdgeNode* p;
	LG = (ListGraph*)malloc(sizeof(ListGraph));
	for (i = 0; i < n; i++) {
		LG->adjList[i].firstEdLGe = NULL;						//给邻接表中所有头结点指针域置初值 
	}
	for (i = 0; i < n; i++) {									//检查邻接矩阵中的每个元素 
		for (j = n - 1; j >= 0; j--) {
			if (A[i][j] != 0) {									//存在一条边 
				p = (EdgeNode*)malloc(sizeof(EdgeNode));		//申请一个结点内存
				p->adjVer = j;									//存放邻接点 
				p->weiLGht = A[i][j];							//存放权值
				p->nextEdLGe = NULL;

				p->nextEdLGe = LG->adjList[i].firstEdLGe;		//头插法 
				LG->adjList[i].firstEdLGe = p;
			}
		}
	}
	LG->n = n;
	LG->e = e;
}

//输出邻接表 
void displayAdjList(ListGraph* LG) {
	int i;
	EdgeNode* p;
	for (i = 0; i < MAXV; i++) {
		p = LG->adjList[i].firstEdLGe;
		printf("%d:", i);
		while (p != NULL) {
			if (p->weiLGht != 32767) {
				printf("%2d[%d]->", p->adjVer, p->weiLGht);
			}
			p = p->nextEdLGe;
		}
		printf(" NULL\n");
	}
}

//输出邻接矩阵
void displayAdjMat(MatGraph MG) {
	int i, j;
	for (i = 0; i < MAXV; i++) {
		for (j = 0; j < MAXV; j++) {
			if (MG.adjMat[i][j] == 0) {
				printf("%4s", "0");
			}
			else if (MG.adjMat[i][j] == 32767) {
				printf("%4s", "∞");
			}
			else {
				printf("%4d", MG.adjMat[i][j]);
			}
		}
		printf("\n");
	}
}

//邻接表转换为邻接矩阵
void ListToMat(ListGraph* LG, MatGraph& MG) {
	int i, j;
	EdgeNode* p;
	for (i = 0; i < MAXV; i++) {
		for (j = 0; j < MAXV; j++) {
			MG.adjMat[i][j] = 0;
		}
	}
	for (i = 0; i < LG->n; i++) {
		p = LG->adjList[i].firstEdLGe;
		while (p != NULL) {
			MG.adjMat[i][p->adjVer] = p->weiLGht;
			p = p->nextEdLGe;
		}
	}
	MG.n = LG->n;
	MG.e = LG->e;
}

//输出多源最短路径
void displayPath(MatGraph MG, int A[MAXV][MAXV], int path[MAXV][MAXV]) {
	int i, j, k;
	int s;
	int aPath[MAXV];										//存放一条最短路径(逆向)
	int d;													//顶点个数
	for (i = 0; i < MG.n; i++) {
		for (j = 0; j < MG.n; j++) {
			if (A[i][j] != INF && i != j) {					//若顶点 i 和 顶点 j 之间存在路径
				printf("从 %d 到 %d 的路径为:", i, j);
				k = path[i][j];
				d = 0;
				aPath[d] = j;								//路径上添加终点
				while (k != -1 && k != i) {					//路劲上添加中间点
					d++;
					aPath[d] = k;
					k = path[i][k];
				}
				d++;
				aPath[d] = i;								//路径上添加起点
				printf("%d", aPath[d]);						//输出起点
				for (s = d - 1; s >= 0; s--) {				//输出路径上其他顶点
					printf("->%d", aPath[s]);
				}
				printf("\t\t");
				printf("路径长度为:%d\n", A[i][j]);
			}
		}
	}
}

//Floyd算法
void Floyd(MatGraph MG) {
	int i, j, k;
	int A[MAXV][MAXV];
	int path[MAXV][MAXV];
	for (i = 0; i < MG.n; i++) {
		for (j = 0; j < MG.n; j++) {
			A[i][j] = MG.adjMat[i][j];
			if (i != j && MG.adjMat[i][j] < INF) {
				path[i][j] = i;							//顶点 i 到顶点 j 有边时
			}
			else {
				path[i][j] = -1;						//顶点 i 到顶点 j 无边时
			}
		}
	}
	for (k = 0; k < MG.n; k++) {						//一次考察所有顶点
		for (i = 0; i < MG.n; i++) {
			for (j = 0; j < MG.n; j++) {
				if (A[i][j] > A[i][k] + A[k][j]) {
					A[i][j] = A[i][k] + A[k][j];		//修改最短路径长度
					path[i][j] = path[k][j];			//修改最短路径
				}
			}
		}
	}
	displayPath(MG, A, path);							//输出最短路径
}

int main() {
	ListGraph* LG;
	MatGraph MG;

	int array[MAXV][MAXV] = {
		{  0,  4,  6,  6,INF,INF,INF},
		{INF,  0,  1,INF,  7,INF,INF},
		{INF,INF,  0,INF,  6,  4,INF},
		{INF,INF,  2,  0,INF,  5,INF},
		{INF,INF,INF,INF,  0,INF,  6},
		{INF,INF,INF,INF,  1,  0,  8},
		{INF,INF,INF,INF,INF,INF,  0}
	};

	int e = 12;
	createAdjListGraph(LG, array, MAXV, e);
	displayAdjList(LG);
	printf("\n");

	ListToMat(LG, MG);
	displayAdjMat(MG);
	printf("\n");

	Floyd(MG);
	printf("\n");

	return 0;
}

五、结果展示

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值