最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)

####最短路径
求最短路径重要性不言而喻,下面直接分析两个算法。
分类:
1:从某个源点到其余个点的最短路径
迪杰斯特拉(Dijkstra)算法
2:每一对之间的最短路径
弗洛伊德(Floyd)算法

####一:存储结构
邻接矩阵(这里不再重复讲了,请参考):
https://blog.csdn.net/weixin_39956356/article/details/80470091

相关代码:

//邻接矩阵
#define INT__MAX	   65000										//最大值65535,表示两顶点没有联系
#define MAX_VERTEX_NUM 20											//最多顶点数

typedef char VertexType;
typedef int  EdgeType;


//顶点信息
typedef struct ArcCell {
	EdgeType wight;
	
}ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];				//二维数组  

																	//弧的信息
typedef struct MGraph {
	VertexType vexs[MAX_VERTEX_NUM];								//顶点向量  
	AdjMatrix  arcs;												//邻接矩阵  
	int vexnum, arcnum;												//图的当前顶点数和弧数  
}MGraph;

####创建有向图
注意一下(有向图):G.arcs[i][j].wight = w;

相关代码:

//建立无向图的邻接矩阵  
void CreatMGraph(MGraph &G)
{
	for (int i = 0; i < G.vexnum; i++) {
		printf("Please enter %d data:", i + 1);
		scanf(" %c", &G.vexs[i]);											//输入顶点值
	}
	for (int i = 0; i<G.vexnum; i++)
		for (int j = 0; j<G.vexnum; j++)
			G.arcs[i][j].wight = INT__MAX;									//邻接矩阵初始化  

	VertexType v1, v2;
	EdgeType w;
	int i, j;
	printf("Please input the two data of arc and wight,for example: A C 5\n");
	for (int k = 0; k < G.arcnum; k++) {
		printf("The %d arc: ", k + 1);
		scanf(" %c", &v1);														//输入第一个顶点
		getchar();																//把输入的空格读走
		v2 = getchar();															//输入弧的第二个顶点
		scanf("%d", &w);														//输入权值
		i = LocateVex(G, v1);													//获取弧的第一个节点位置
		j = LocateVex(G, v2);													//获取弧的第二个节点位置

		G.arcs[i][j].wight = w;													//把权值存放在邻接矩阵中
	}
}

####二:迪杰斯特拉(Dijkstra)算法,带输出路径
自己将这个算法分成两部分:
一:最短路径长度
二:最短路径
####首先,我们先看最短路径长度问题
1:这里需要引入一个辅助数组Dij[],每次遍历一遍数组得到本次数组中最小值及所在位置。
这里写图片描述
2:找到最小值,并入S集
这里写图片描述
3:更新新的路径值
判断条件:还没有并入S集,且有更小的路径值
if (!isGetShortestPath[w] && (min + G.arcs[minIndex][w].wight < Dij[w]))
这里写图片描述
####其次,最短路径
我拿一个图来讲解,整个流程。不防举A->E。它的最短路径是A->C->E,我们分析下
1:这里需要引入一个辅助Code数组code[][],记录走过的最短路径,初始值不防赋值为’!’。
这里写图片描述
2:清空E结点该行
3:把C结点路径拷贝到E中
4:在第一个 ! ,添加尾结点E
这里写图片描述

图6及输出:
这里写图片描述
这里写图片描述

补充:这里再多说一句,动态二维数组分配
######先分配行,后分配列

typedef char **shortestCode;										//最短路径编码
/******************************************************************
	**	动态分配二维数组
	*******************************************************************/
	code = (shortestCode)malloc(G.vexnum * sizeof(char *));						//分配G.vexnum行
	for (int i = 0; i < G.vexnum; ++i) {										//为每行分配G.vexnum列
		code[i] = (char *)malloc(G.vexnum * sizeof(char));
	}

相关代码

/***********************************************************************************************
**							迪杰斯特拉(Dijkstra)算法
**	v0  :任给一个起始点
** &code:最短路径线路(这里的&是必须的,如果没有,相当于传进来没有初始化的实体,编译器报错)
************************************************************************************************/
void ShortestPath_DIJ(MGraph &G, char v0, shortestCode &code)
{
	int v0Index = LocateVex(G, v0);
	bool *isGetShortestPath = (bool *)malloc(G.vexnum * sizeof(bool));			//是否并入S集
	int *Dij = (int *)malloc(G.vexnum * sizeof(int));							//最短路径和

	/******************************************************************
	**	动态分配二维数组
	*******************************************************************/
	code = (shortestCode)malloc(G.vexnum * sizeof(char *));						//分配G.vexnum行
	for (int i = 0; i < G.vexnum; ++i) {										//为每行分配G.vexnum列
		code[i] = (char *)malloc(G.vexnum * sizeof(char));
	}
	 
	for (int i = 0; i < G.vexnum; i++) {										//初始所有点都不并入S集,把顶点的权值赋给Dij[]
		isGetShortestPath[i] = false;
		Dij[i] = G.arcs[v0Index][i].wight;

		for (int w = 0; w < G.vexnum; w++)
			code[i][w] = '!';													//设code[][]初值为!,即没有路径   
		if (Dij[i] < INT__MAX)													
		{
			code[i][0] = v0;													//到i最短路径经过的第一个顶点是v0
			code[i][1] = G.vexs[i];												//到i最短路径经过的第二个顶点是i
		}
	}

	isGetShortestPath[v0Index] = true;											//把顶点并入S集
	
	int min, minIndex;
	for (int i = 1; i < G.vexnum; i++) {
		min = INT__MAX;															//不妨把min放置为最大值
		/******************************************************************
		**	获取Dij数组中最小值-----这是其他还没有并入S集 路径上的一个子集
		**	对应的数组位置----------为方便下面从这结点出发的弧遍历找最小值
		*******************************************************************/
		for (int w = 0; w < G.vexnum; w++) {
			if (!isGetShortestPath[w])
				if (Dij[w] < min)
				{
					minIndex = w;
					min = Dij[w];
				}
		}

		isGetShortestPath[minIndex] = true;										//把位置为minIndex的结点并入S集

		for (int w = 0; w < G.vexnum; w++) {									//如果有更小的路径,替换原来的路径和最小值
			if (!isGetShortestPath[w] && (min + G.arcs[minIndex][w].wight < Dij[w]))
			{
				Dij[w] = min + G.arcs[minIndex][w].wight;						//更新Dij[]值

				for (int j = 0; j < G.vexnum; j++) {							//如果Dij[]值改变,新节点路径清空
					code[w][j] = '!';
				}
				for (int j = 0; j < G.vexnum; j++) {
					code[w][j] = code[minIndex][j];								//把minIndex结点路径全部拷贝到w中去
					if (code[w][j] == '!')
					{
						code[w][j] = G.vexs[w];									//在遇到第一个'!',将新节点放在后面
						break;
					}
				}
			}
		}
	}

	printf("\n迪杰斯特拉(Dijkstra)最短路径长度与最短路径如下所示:\n");														
	for (int i = 1; i < G.vexnum; i++) {
		if (Dij[i] != INT__MAX)
		{
			printf("从顶点 %c ---> %c 最短路径长度为:%d\t最短路径为:", v0, G.vexs[i], Dij[i]);
			for(int j = 0; j < G.vexnum; j++)
			{
				if(code[i][j] != '!')
					printf(" %c",code[i][j]);									//输出最短路径
			}
			printf("\n");
		}
		else if (Dij[i] == INT__MAX)
		{
			printf("从顶点 %c ---> %c 不可到达!!!\n", v0, G.vexs[i]);		//不存在最短路径
		}
	}

	free(Dij);																	//释放Dij[][]空间
	free(isGetShortestPath);													//释放isGetShortestPath[]空间
}

####程序输出
图6及输出:
这里写图片描述
图5及输出:
这里写图片描述

####三:弗洛伊德(Floyd)算法,带输出路径
和dijkstra算法一样,自己将这个算法分成两部分:
一:最短路径长度
二:最短路径
####首先,我们先看最短路径长度问题
这个算法的最短路径长度核心代码只有5行
1:就是看走哪边短!!!如果短就替换。
这里写图片描述

####其次,最短路径(弗洛伊德输出路径要麻烦些)
####一:路径的创建
####1:Path[ ][ ]初始化
矩阵Path的初值则为各个边的终点顶点-----相当于直接从v到w(上面两种方案的第一种)
这里写图片描述
####2:Path[ ][ ]逐渐储存路径
当且仅当通过“另外一个点”的时候,有更短路径,即更新路径Path[v][w],把“另外一个点”放进Path[v][w]
这里写图片描述
这里举个例子:比如A->D,路径是A->E->D
这里写图片描述
####二:路径的输出
这里主要是是一个循环,只到输出字母是终点为止。
这里写图片描述
不防先人为的输出
起点
,和第一个点,剩下的交给while循环
这里写图片描述
最后举个例子:比如A->F,路径是A->E->D->F
这里写图片描述
需要注意的是:Floyd-Warshall算法不能解决带有“负权回路”(或者叫“负权环”)的图
问题。因为带有“负权回路”的图没有最短路。比如:
这里写图片描述

相关代码

/*************************************************************************************************************
**							弗洛伊德(Floyd)算法
**	核心:是一种尝试的想法,从v到w只有两种方案:
			1:直接从v到w,即DFloyd[v][w]
			2:经过“另外的一个点”u,从v到w,即DFloyd[v][u] + DFloyd[u][w]
		  比较两种方案,取更小的
			1:比较	   :DFloyd[v][u] + DFloyd[u][w] < DFloyd[v][w]
			2:取更小的:DFloyd[v][w] = DFloyd[v][u] + DFloyd[u][w]
**	这里的Path矩阵的构造很巧,关于Path矩阵代码仅仅只有几行而已!!!下面分析Path矩阵
	1:矩阵Path的初值则为各个边的终点顶点-----相当于直接从v到w(上面两种方案的第一种)
	2:当且仅当通过“另外一个点”的时候,有更短路径,即更新路径Path[v][w],把“另外一个点”放进Path[v][w]里
** 路径输出
	1:不防把起点(G.vexs[i]),和Path[i][j]先输出(Path[i][j]是路径的第一个点)
	2:循环输出直到(Path[temp][j] == j)为止!!!
**	自己可能还没有讲清楚,不用担心我在画个图解释下
	请参考:https://mp.csdn.net/mdeditor/80579845
****************************************************************************************************************/
void ShortestPath_FLOYD(MGraph &G, shortestCode &Path)
{
	//最短路径长度DFloyd[][]
	int **DFloyd = (int **)malloc(G.vexnum * sizeof(int *));					//动态二维数组DFloyd[][],分配G.vexnum行
	for (int i = 0; i < G.vexnum; ++i) {										//为每行分配G.vexnum列
		DFloyd[i] = (int *)malloc(G.vexnum * sizeof(int));
	}

	//最短路径Path[][]
	Path = (shortestCode)malloc(G.vexnum * sizeof(char *));						//动态二维数组Path[][],分配G.vexnum行
	for (int i = 0; i < G.vexnum; ++i) {											//为每行分配G.vexnum列
		Path[i] = (char *)malloc(G.vexnum * sizeof(char));
	}

	for (int v = 0; v < G.vexnum; v++)
		for (int w = 0; w < G.vexnum; w++) {
			if (v == w)															//这里把主对角线权值变为0,不然主对角线会错,因为之前赋值的∞,举个例子B->B,现有B->C + C->B之和肯定小于∞,所以主对角线发生错误
				G.arcs[v][w].wight = 0;
			DFloyd[v][w] = G.arcs[v][w].wight;									//把初始值存入DFloyd[][]
			Path[v][w] = G.vexs[w];												//矩阵Path的初值则为各个边的终点顶点
		}
	printf("Path矩阵:\n");
	for (int i = 0; i < G.vexnum; i++) {
		for (int j = 0; j < G.vexnum; j++)
			printf("%c\t", Path[i][j]);
		printf("\n");
	}
	/******************************************************************
	**	u:经过另外一个点
	**	v:起点
	**	w:终点
	*******************************************************************/
	for (int u = 0; u < G.vexnum; u++)
		for (int v = 0; v < G.vexnum; v++)
			for (int w = 0; w < G.vexnum; w++)
				if (DFloyd[v][u] + DFloyd[u][w] < DFloyd[v][w]) {				//比较两种方案,取更小的
					DFloyd[v][w] = DFloyd[v][u] + DFloyd[u][w];
					Path[v][w] = Path[v][u];									//更新Path矩阵
				}

	printf("\n\t\t弗洛伊德(Floyd)算法\nD矩阵:\n");
	for (int i = 0; i < G.vexnum; i++){
		for (int j = 0; j < G.vexnum; j++) 			
			if (DFloyd[i][j] < INT__MAX)
				printf("%d\t", DFloyd[i][j]);
			else
				printf("∞\t");
		printf("\n");
	}

	printf("Path矩阵:\n");
	for (int i = 0; i < G.vexnum; i++) {
		for (int j = 0; j < G.vexnum; j++)
			printf("%c\t", Path[i][j]);
		printf("\n");
	}

	printf("\n弗洛伊德(Floyd)最短路径长度与最短路径如下所示:\n");
	/***************************************************************************************
	**			这里输出除自己到自己的所有路径
	**	temp:通过Path[i][j] ---找到--> Path[Path[i][j]][j]<---判断是否相等-->j
	****************************************************************************************/
	for (int i = 0; i < G.vexnum; i++)
	{
		int temp = 0;
		for (int j = 0; j < G.vexnum; j++) {
			if(DFloyd[i][j] != INT__MAX)
			{
				if (j != i)															//这里不需要输出自己到自己的路径
					printf("从顶点 %c ---> %c 最短路径长度为:%d\t最短路径为: %c %c", G.vexs[i], G.vexs[j], DFloyd[i][j], G.vexs[i], Path[i][j]);	//不防把起点(G.vexs[i]),和Path[i][j]先输出(Path[i][j]是路径的第一个点)
			}else if (DFloyd[i][j] == INT__MAX)
			{
				printf("从顶点 %c ---> %c 不可到达!!!", G.vexs[i], G.vexs[j]);	//不存在最短路径
			}

			temp = LocateVex(G, Path[i][j]);
			while (temp != j) {														//循环输出直到(Path[temp][j] == j)为止!!!
				printf(" %c", Path[temp][j]);										//输出最短路径
				temp = LocateVex(G, Path[temp][j]);
			}
			if (j != i)																//自己到自己不需要换行
				printf("\n");														
		}	
		printf("\n");																//到某个点路径都显示了,换行
	}

	free(DFloyd);																	//释放DFloyd[][]空间
	free(Path);																		//释放Path[][]空间
}

####程序输出
图5及输出:
这里写图片描述

图6及输出:
这里写图片描述

####主函数

#include "stdafx.h"
#include "dijkatra.h"

int main()
{
	MGraph G;																			//有向图的邻接矩阵
	shortestCode code;																	//编码--输出路径

	printf("Please enter vexnum and arcnum: ");
	scanf("%d %d", &G.vexnum, &G.arcnum);												//输入结点数,弧数

	CreatMGraph(G);																		//建立无向图的邻接矩阵 
	printf("\nTne output of Adjacency Matrix:\n\n");
	printMatrixGraph(G);																//输出邻接矩阵

	ShortestPath_DIJ(G, G.vexs[0], code);												//迪杰斯特拉(Dijkstra)算法--求得最短路径长度与最短路径
	ShortestPath_FLOYD(G, code);														//弗洛伊德(Floyd)算法--求得最短路径长度与最短路径
	return 0;
}

####四:感谢与源代码(VS2017)
1:感谢一下波主文章对我的帮助,我相信他们的文章对你也会有帮助!!!
https://blog.csdn.net/qq_34374664/article/details/52261672
https://blog.csdn.net/txl199106/article/details/44980923
2:源代码(VS2017)
链接: https://pan.baidu.com/s/15kSoQhm2CTFHPN32OMez6g 提取码: w8va
3:如果你觉得本文还不错,请务必说明出处。

  • 46
    点赞
  • 245
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值