Floyd算法,图的最短路径,c/c++描述

  求图中任意两顶点间的最短路径。可以参考这篇文章Floyd - Warshall(弗洛伊德算法)
https://blog.csdn.net/yuewenyao/article/details/81021319
  Dijkstra算法是求解单源点最短路径。依次把图中所有顶点作为源点,也可以得到图中任意两顶点间的最短路径。
  Floyd算法的依据是这个公式dist[i][j] = min(dist[i][j] , dist[i][k] + dist[k][j])。
  关于最短路径的几个关键知识点:
  (1)最短路径中的任意两个顶点间的路径都是最短路径。这个可以由反证法来证明。如A——>B——>C是最短路径,若A——>B不是最短路径,那么包括A——>B的A——>B——>C也不是最短路径。
  (2)最短路径中不允许有重复的顶点。不同的顶点只可以出现一次。
  Floyd算法的经典表示:

for(int k = 0 ; k < numVertexes ; k++)
		for(row = 0 ; row < numVertexes ; row++)
			for(column = 0 ; column < numVertexes ; column++)
				if (row != column && row != k && k != column &&
					distance[row][column] > distance[row][k] + distance[k][column]) {
					distance[row][column] = distance[row][k] + distance[k][column];
					
					allShortestPath[row][column] = k;
				}

  关于该算法的几个关键点的解释:
  (1)对于图的所有路径的遍历是由dist[i][j]数组来表示和完成的,起点是 row,终点是 column 。对应矩阵数组行列下标。
  (2)为什么把 k 循环放在最外面 ? 意味着对图中所有路径,尝试插入顶点1 (k = 1),再尝试对图中所有路径插入顶点1 和 2 (k = 2),再尝试对图中所有路径插入顶点 1 、2 和 3(k = 3),最终意味着尝试把图中所有顶点插入路径。但路径中是不包括重复顶点的。k值在变化,但k值经历过的值,已经写入distance二维数组,其影响一直存在。这是开头引用的那个链接文章里给出的重大发现和知识点。
  (3)k循环可以放在最内层么? 答案是不可以,如果k循环放在最内层,意味着每次往路径中只插入一个不同的顶点,以寻求最短路径。但我们知道,最短路径不只是最多三个顶点的。所以这么做肯定是错误的。
  (4)row 循环 和 column 循环的顺序,是否会对图中所有最短路径的查找造成影响? 或者说,因为 row 和 column 循环的顺序不同,是否会对图最短路径的遍历结果产生影响 ? 答案是不会的。不管 row 和 column 递增递减的顺序怎么变化,只要对每一个 k 值,对图中所有最短路径的遍历做到不重不漏即可。因为最短路径中没有重复顶点。比如考察 k = 3 时的情况,我们已经计算和考虑了 k = 1 和 k = 2 对最短路径的影响。已有了最短路径 5——>2——>3,3——>1——>4。当3 的加入使路径更短时,路径变为(54) = (53)+(34) = 5——>2——>3——>1——>4,但单独对于路径(53)和(34)来讲是不会重复加入顶点 3 的。而已经被 k 遍历过的顶点,其影响已经体现在distance数组里每个元素里了。对于当前 k 值正在遍历的待插入顶点,路径数组distance[][]中每个元素的值都是可靠的,可被引用的,不受当前k值影响的。k值影响的是下一次的k循环,而不是当前的 k 循环。所以对图的所有最短路径的遍历,不区分先后顺序。对于path[][]数组存储的是终点的前一个顶点的情况,该值改为k当前的值,3 。 该数组里凡是以3 为起点或终点的元素值,并不会发生改变。
  程序实现时的有一方面的处理:
  图的最短路径长度由distance[][]数组保存,对应路径由数组path[][]保存,path[][]中的每个元素可以是一个结构体变量,由一个存储下标的数组成员和数组长度成员,下标数组保存了路径中经过的所有顶点的下标。还可以如同Dijkstra算法的处理。path[][]数组保存路径中终点的前一个顶点,当保存的是起点时,意味着已经找到了完整路径。后一种保存方式节省空间,但在输出完整路径时需要结合算法,不太直观。
  Floyd算法是为了纪念伟大的科学家Floyd 。
  以上两种path[][]数组的实现方式,本文分别给出其完整代码。其结果应该是相同的。
  先是path数组存储完整路径下标的所有代码,对应bilibili懒猫老师的讲解,先是main函数所在源文件:

#include<iostream>
#include<stdio.h>
using namespace std;
#define NUMVERTEX 4
#define INFINI 10000

struct GraphAdjaMatrix {
	char vertexes[NUMVERTEX];
	int edges[NUMVERTEX][NUMVERTEX];
	int numVertexes;
	int numEdges;
};

struct ShortestPath {
	int indicesOfVertexes[NUMVERTEX];
	int arrayLength;
};

extern void createGraphAdjMatrix(GraphAdjaMatrix &graphAdjMatrix,
			int numVertexes,int numEdges,int edges[][NUMVERTEX],char vertexes[]);
extern void dispalyGraphAdjMatrix(GraphAdjaMatrix &graphAdjMatrix);
extern void FloydAllShortestPath(GraphAdjaMatrix& graphAdjMatrix,
	int distance[][NUMVERTEX],ShortestPath allShortestPath[][NUMVERTEX]);
extern void dispalyFloydPath(GraphAdjaMatrix& graphAdjMatrix,
	int distance[][NUMVERTEX], ShortestPath allShortestPath[][NUMVERTEX]);


int main() {
	GraphAdjaMatrix graphAdjMatrix ;
	int numEdges = 8;
	int edges[][NUMVERTEX] = {	{0,5,INFINI,7},
								{INFINI,0,4,2},
								{3,3,0,2},
								{INFINI,INFINI,1,0} };
	char vertexes[] = { '0','1','2','3' };

	createGraphAdjMatrix(graphAdjMatrix,NUMVERTEX,numEdges,edges,vertexes);

	dispalyGraphAdjMatrix(graphAdjMatrix);
	cout << endl;

	int distance[NUMVERTEX][NUMVERTEX];
	ShortestPath allShortestPath[NUMVERTEX][NUMVERTEX];

	FloydAllShortestPath(graphAdjMatrix,distance,allShortestPath);
	cout << endl << "all Floyd path : " << endl;
	dispalyFloydPath(graphAdjMatrix,distance,allShortestPath);

	return 0;
}

接着是各函数所在源文件代码:

#include<iostream>
#include<stdio.h>
using namespace std;
#define NUMVERTEX 4
#define INFINI 10000

struct GraphAdjaMatrix {
	char vertexes[NUMVERTEX];
	int edges[NUMVERTEX][NUMVERTEX];
	int numVertexes;
	int numEdges;
};

struct ShortestPath {
	int indicesOfVertexes[NUMVERTEX];
	int arrayLength;
};

void createGraphAdjMatrix(GraphAdjaMatrix &graphAdjMatrix,
	int numVertexes, int numEdges, int edges[][NUMVERTEX], char vertexes[]) {
	graphAdjMatrix.numVertexes = numVertexes;
	graphAdjMatrix.numEdges = numEdges;
	
	for (int i = 0; i < numVertexes; i++)
		graphAdjMatrix.vertexes[i] = vertexes[i];

	for (int row = 0; row < numVertexes; row++)
		for (int column = 0; column < numVertexes; column++)
			graphAdjMatrix.edges[row][column] = edges[row][column];
}

void dispalyGraphAdjMatrix(GraphAdjaMatrix &graphAdjMatrix) {
	cout << "adjacensy matrix :" << endl;
	int row,column;
	printf("%3c",' ');
	for (row = 0; row < graphAdjMatrix.numVertexes; row++)
		printf("%3c",graphAdjMatrix.vertexes[row]);
	printf("\n");
	for (row = 0; row < graphAdjMatrix.numVertexes; row++) {
		printf("%-3c", graphAdjMatrix.vertexes[row]);
		for (column = 0; column < graphAdjMatrix.numVertexes; column++)
			if (graphAdjMatrix.edges[row][column] == INFINI)
				printf("%3s", "∞");
			else
				printf("%3d",graphAdjMatrix.edges[row][column]);
		cout << endl;
	}
}

void FloydAllShortestPath(GraphAdjaMatrix& graphAdjMatrix,
	int distance[][NUMVERTEX], ShortestPath allShortestPath[][NUMVERTEX]) {
	
	int numVertexes = graphAdjMatrix.numVertexes;
	int row, column;

	for (row = 0; row < numVertexes; row++)
		for (column = 0; column < numVertexes; column++) 
			if(row != column){
			distance[row][column] = graphAdjMatrix.edges[row][column];
			
			allShortestPath[row][column].indicesOfVertexes[0] = row;
			allShortestPath[row][column].indicesOfVertexes[1] = column;
			allShortestPath[row][column].arrayLength = 2;
		}

	for(int k = 0 ; k < numVertexes ; k++)
		for(row = 0 ; row < numVertexes ; row++)
			for(column = 0 ; column < numVertexes ; column++)
				if (row != column && row != k && k != column &&
					distance[row][column] > distance[row][k] + distance[k][column]) {
					distance[row][column] = distance[row][k] + distance[k][column];
					
					int length;
					for (length = 1; length < allShortestPath[row][k].arrayLength; length++)
						allShortestPath[row][column].indicesOfVertexes[length] =
						allShortestPath[row][k].indicesOfVertexes[length];

					for (int i = 1; i < allShortestPath[k][column].arrayLength; i++) {
						allShortestPath[row][column].indicesOfVertexes[length] =
							allShortestPath[k][column].indicesOfVertexes[i];

						length++;
					}
				
					allShortestPath[row][column].arrayLength = length;
				}
}

void dispalyFloydPath(GraphAdjaMatrix& graphAdjMatrix,
	int distance[][NUMVERTEX], ShortestPath allShortestPath[][NUMVERTEX]) {
	int row, column;
	for(row = 0 ; row < graphAdjMatrix.numVertexes ; row++)
		for (column = 0; column < graphAdjMatrix.numVertexes; column++) 
			if(row != column){
				cout << "path from " << graphAdjMatrix.vertexes[row] << " to ";
				cout << graphAdjMatrix.vertexes[column] << " , length : ";
				printf("%-3d    path : ",distance[row][column]);
			
				for (int i = 0; i < allShortestPath[row][column].arrayLength; i++)
					cout << graphAdjMatrix.vertexes[allShortestPath[row][column].indicesOfVertexes[i]]<<" ";
			
				cout << endl;
			}
}

接着是path数组存储终点前一个顶点的下标的所有代码,先是main函数的源文件:

#include<iostream>
#include<stdio.h>
using namespace std;
#define NUMVERTEX 4
#define INFINI 10000

struct GraphAdjaMatrix {
	char vertexes[NUMVERTEX];
	int edges[NUMVERTEX][NUMVERTEX];
	int numVertexes;
	int numEdges;
};

extern void createGraphAdjMatrix(GraphAdjaMatrix &graphAdjMatrix,
			int numVertexes,int numEdges,int edges[][NUMVERTEX],char vertexes[]);
extern void dispalyGraphAdjMatrix(GraphAdjaMatrix &graphAdjMatrix);
extern void FloydAllShortestPath(GraphAdjaMatrix& graphAdjMatrix,
	int distance[][NUMVERTEX],int allShortestPath[][NUMVERTEX]);
extern void dispalyFloydPath(GraphAdjaMatrix& graphAdjMatrix,
	int distance[][NUMVERTEX],int allShortestPath[][NUMVERTEX]);


int main() {
	GraphAdjaMatrix graphAdjMatrix ;
	int numEdges = 8;
	int edges[][NUMVERTEX] = {	{0,5,INFINI,7},
								{INFINI,0,4,2},
								{3,3,0,2},
								{INFINI,INFINI,1,0} };
	char vertexes[] = { '0','1','2','3' };

	createGraphAdjMatrix(graphAdjMatrix,NUMVERTEX,numEdges,edges,vertexes);

	dispalyGraphAdjMatrix(graphAdjMatrix);
	cout << endl;

	int distance[NUMVERTEX][NUMVERTEX];
	int allShortestPath[NUMVERTEX][NUMVERTEX];

	FloydAllShortestPath(graphAdjMatrix,distance,allShortestPath);
	cout << endl << "all Floyd path : " << endl;
	dispalyFloydPath(graphAdjMatrix,distance,allShortestPath);

	return 0;
}

接着是各函数所属源文件:

#include<iostream>
#include<stdio.h>
using namespace std;
#define NUMVERTEX 4
#define INFINI 10000

struct GraphAdjaMatrix {
	char vertexes[NUMVERTEX];
	int edges[NUMVERTEX][NUMVERTEX];
	int numVertexes;
	int numEdges;
};

void createGraphAdjMatrix(GraphAdjaMatrix &graphAdjMatrix,
	int numVertexes, int numEdges, int edges[][NUMVERTEX], char vertexes[]) {
	graphAdjMatrix.numVertexes = numVertexes;
	graphAdjMatrix.numEdges = numEdges;
	
	for (int i = 0; i < numVertexes; i++)
		graphAdjMatrix.vertexes[i] = vertexes[i];

	for (int row = 0; row < numVertexes; row++)
		for (int column = 0; column < numVertexes; column++)
			graphAdjMatrix.edges[row][column] = edges[row][column];
}

void dispalyGraphAdjMatrix(GraphAdjaMatrix &graphAdjMatrix) {
	cout << "adjacensy matrix :" << endl;
	int row,column;
	printf("%3c",' ');
	for (row = 0; row < graphAdjMatrix.numVertexes; row++)
		printf("%3c",graphAdjMatrix.vertexes[row]);
	printf("\n");
	for (row = 0; row < graphAdjMatrix.numVertexes; row++) {
		printf("%-3c", graphAdjMatrix.vertexes[row]);
		for (column = 0; column < graphAdjMatrix.numVertexes; column++)
			if (graphAdjMatrix.edges[row][column] == INFINI)
				printf("%3s", "∞");
			else
				printf("%3d",graphAdjMatrix.edges[row][column]);
		cout << endl;
	}
}

void FloydAllShortestPath(GraphAdjaMatrix& graphAdjMatrix,
	int distance[][NUMVERTEX], int allShortestPath[][NUMVERTEX]) {
	
	int numVertexes = graphAdjMatrix.numVertexes;
	int row, column;

	for (row = 0; row < numVertexes; row++)
		for (column = 0; column < numVertexes; column++) 
			if(row != column){
				distance[row][column] = graphAdjMatrix.edges[row][column];
				
				allShortestPath[row][column] = row;
			}

	for(int k = 0 ; k < numVertexes ; k++)
		for(row = 0 ; row < numVertexes ; row++)
			for(column = 0 ; column < numVertexes ; column++)
				if (row != column && row != k && k != column &&
					distance[row][column] > distance[row][k] + distance[k][column]) {
					distance[row][column] = distance[row][k] + distance[k][column];
					
					allShortestPath[row][column] = k;
				}
}

void dispalyFloydPath(GraphAdjaMatrix& graphAdjMatrix,
	int distance[][NUMVERTEX],int allShortestPath[][NUMVERTEX]) {
	int row, column;
	for(row = 0 ; row < graphAdjMatrix.numVertexes ; row++)
		for (column = 0; column < graphAdjMatrix.numVertexes; column++) 
			if(row != column){
				cout << "path from " << graphAdjMatrix.vertexes[row] << " to ";
				cout << graphAdjMatrix.vertexes[column] << " , length : ";
				printf("%-3d    path : ",distance[row][column]);
			
				int length = 0, indices[NUMVERTEX],columnY = column;
				indices[length] = column;
				while (allShortestPath[row][columnY] != row) {
					columnY = allShortestPath[row][columnY];
					length++;
					indices[length] = columnY;
				}
				length++;
				indices[length] = row;

				for (; length >= 0; length--)
					cout << graphAdjMatrix.vertexes[indices[length]] << " ";

				cout << endl;
			}
}

两种写法的结果都是相同的,还有课本里的对应的图和标准答案,如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
谢谢阅读。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值