数据结构C++——最短路径之Dijkstra算法和Floyd算法

数据结构C++——最短路径之Dijkstra算法和Floyd算法

一、最短路径之Dijkstra算法

①Dijkstra算法的实现原理

算法的实现要引入三个辅助数组:
①:S[i]:记录从源点v0到终点v1是否已被确定最短路径长度,true表示确定,false表示尚未确定。
②:Path[i]:记录从源点v0到终点vi的当前最短路径上vi的直接前驱顶点序号。其初值为:如果从v0到vi有弧,则Path[i]为v0,否则为-1
③:D[i]:记录从源点v0到终点vi的当前最短路径长度。其初值为:如果从v0到vi有弧,则D[i]为弧上的权值,否则为无穷大

②Dijkstra算法的代码实现

Dijkstra算法的代码实现

Dijkstra算法的实现思路:
1:定义Dijstra算法需要用到的三个辅助数组S[MVNum]、Path[MVNum]、D[MVNum]。
2:初始化三个辅助数组单元值,S数组单元值均初始化为false,表明结点到源点的距离均未确定;D数组单元值均初始化为图的邻接矩阵中的边权值;若v0和v之间有弧则将对应的Path[v]值初始化为v0,否则初始化为-1
3:将源点的对应的S数组单元值初始化为true,对应的D数组单元值初始化为0
4:选择出距离源点最近的结点编号,将其对应的S数组单元值置为true;假设此结点为v1,则检查v0路过v1结点再到其他结点的多条边权值和是否小于从v0直接到其他结点的边权值,相对应地更新D数组和Path数组的单元值

三个辅助数组的定义:

/*-----------迪杰斯特拉算法辅助数组的建立----------*/
bool S[MVNum];
int Path[MVNum];
int D[MVNum];

最短路径之Dijkstra算法的代码

/*-----------最短路径之迪杰斯特拉算法------------*/
void ShortestPath_DIJ(AMGraph G, int v0) {
	//用Dijkstra算法求有向图G的v0顶点到其余顶点的最短路径
	int n = G.vexnum;//n为G中顶点的个数
	for (int v = 0; v < n; v++) {
		S[v] = false;//S初始化为空集
		D[v] = G.arcs[v0][v];//将v0到各个终点的最短路径长度初始化为弧上的权值
		if (D[v] < MaxInt) Path[v] = v0;//如果v0和v之间有弧,则将v的前驱置为v0
		else Path[v] = -1;//如果v0和v之间无弧,则将v的前驱置为-1
	}
	S[v0] = true;//将v0加入S
	D[v0] = 0;//源点到源点的距离为0
	/*----------初始化结束,开始主循环,每次求得v0到某个顶点v的最短路径,将v加到S集-------*/
	for (int i = 1; i < n; i++) {
		//对其余n-1个顶点,依次进行计算
		int min = MaxInt;
		int v = 0;
		for (int w = 0; w < n; w++)
			if (!S[w] && D[w] < min) {
				v = w; min = D[w];//选择一条当前的最短路径,终点为v
			}
			S[v] = true;//将v加入S
			for (int w = 0; w < n; w++) {//更新从v0出发到集合V-S上所有顶点的最短路径长度
				if (!S[w] && (D[v] + G.arcs[v][w] < D[w])) {
					D[w] = D[v] + G.arcs[v][w];//更新D[w]
					Path[w] = v;//更改w的前驱为v
				}
			}
	}
}

③测试的完整代码

最短路径基于图的邻接矩阵或邻接表的知识,由于此内容笔者已经在之前的文章中介绍过,这里不再过多赘述,对该内容不熟悉的读者欢迎移步此文章,共同学习!:链接: 数据结构C++——图的邻接矩阵和邻接表.
同时关于Dijkstra算法的实现笔者参考了这篇文章,笔者就是从中找到的思路,写的真的很不错,向大佬致敬!:C/C++ 最短路径-Dijkstra算法 (路径的保存和输出).
测试代码:

#include<iostream>
using namespace std;
/*-------图的邻接矩阵存储表示-----*/
#define MVNum 100//最大顶点数
#define MaxInt 32767//无穷大
#define OK 1
#define ERROR 0
typedef int Status;
typedef string VerTexType;//假设顶点的数据类型为字符型
typedef int ArcType;//假设边的权值类型为整型

bool visited[MVNum];//定义标志数组

typedef struct {
	VerTexType vexs[MVNum];//顶点表
	ArcType arcs[MVNum][MVNum];//邻接矩阵
	int vexnum, arcnum;//图的当前点数和边数
}AMGraph;
/*-----------迪杰斯特拉算法辅助数组的建立----------*/
bool S[MVNum];
int Path[MVNum];
int D[MVNum];
/*---------确定某顶点在G中的位置下标-----------*/
int LocateVex(AMGraph G, VerTexType u) {
	int i;
	for (int i = 0; i < G.vexnum; i++)
		if (u == G.vexs[i]) return i;
	return -1;
}
/*---------采用邻接矩阵表示法创建无向网-------*/
void CreateUDN(AMGraph& G) {
	//采用邻接矩阵表示法,创建无向网
	int i = 0, j = 0, k = 0;
	cin >> G.vexnum >> G.arcnum;//输入总顶点数,总边数
	for (i = 0; i < G.vexnum; i++)//依次输入点的信息
		cin >> G.vexs[i];
	for (i = 0; i < G.vexnum; i++)//初始化邻接矩阵
		for (j = 0; j < G.vexnum; j++)
			G.arcs[i][j] = MaxInt;
	for (k = 0; k < G.arcnum; k++) {
		VerTexType v1, v2;//定义两个顶点v1,v2
		int w = 0;
		cin >> v1 >> v2 >> w;
		i = LocateVex(G, v1);
		j = LocateVex(G, v2);//确定v1和v2在G中的位置,即顶点数组的下标
		G.arcs[i][j] = w;
	}
}
/*-----------最短路径之迪杰斯特拉算法------------*/
void ShortestPath_DIJ(AMGraph G, int v0) {
	//用Dijkstra算法求有向图G的v0顶点到其余顶点的最短路径
	int n = G.vexnum;//n为G中顶点的个数
	for (int v = 0; v < n; v++) {
		S[v] = false;//S初始化为空集
		D[v] = G.arcs[v0][v];//将v0到各个终点的最短路径长度初始化为弧上的权值
		if (D[v] < MaxInt) Path[v] = v0;//如果v0和v之间有弧,则将v的前驱置为v0
		else Path[v] = -1;//如果v0和v之间无弧,则将v的前驱置为-1
	}
	S[v0] = true;//将v0加入S
	D[v0] = 0;//源点到源点的距离为0
	/*----------初始化结束,开始主循环,每次求得v0到某个顶点v的最短路径,将v加到S集-------*/
	for (int i = 1; i < n; i++) {
		//对其余n-1个顶点,依次进行计算
		int min = MaxInt;
		int v = 0;
		for (int w = 0; w < n; w++)
			if (!S[w] && D[w] < min) {
				v = w; min = D[w];//选择一条当前的最短路径,终点为v
			}
			S[v] = true;//将v加入S
			for (int w = 0; w < n; w++) {//更新从v0出发到集合V-S上所有顶点的最短路径长度
				if (!S[w] && (D[v] + G.arcs[v][w] < D[w])) {
					D[w] = D[v] + G.arcs[v][w];//更新D[w]
					Path[w] = v;//更改w的前驱为v
				}
			}
	}
}
/*-----------递归输出最短路径----------*/
void find(int x) {
	if (Path[x] == 0) {
		cout << 0;
	}
	else {
		find(Path[x]);
	}
	cout << "->" << x;
	return;
}
/*--------输出结果----------*/
void PrintResult(AMGraph G) {
	cout << "点v0到其他各点的最短路程为:" << endl;
	for (int i = 0; i < G.vexnum; i++) {
		cout << D[i] << " ";
	}
	cout << endl;
	for (int i = 1; i < G.vexnum; i++) {
		if (D[i] == MaxInt) continue;
		cout << "起点v0到v" << i << "的路径为:";
		find(i);
		cout << endl;
	}
}
/*-----------主函数-----------*/
int main() {
	AMGraph G;
	CreateUDN(G);
	ShortestPath_DIJ(G, 0);
	PrintResult(G);
	return 0;
}
输入:
6 8
v0 v1 v2 v3 v4 v5
v0 v5 100
v0 v4 30
v0 v2 10
v1 v2 5
v2 v3 50
v3 v5 10
v4 v3 20
v4 v5 60

输入数据构造的有向图:
在这里插入图片描述

输出 :

在这里插入图片描述
此处的32767是无穷大,表明v0顶点到v1顶点之间不存在路径

二、最短路径之Floyd算法

①Floyd算法的实现原理

②Floyd算法的代码实现

Floyd算法的代码实现

Floyd算法的实现思路
1:将D[i][j]数组的单元值均置为G的邻接矩阵的单元值
2:若i号结点和j号结点之间有弧,则将j的前驱置为i,即Path[i][j]=i
3:三层循环不断加入中间结点,检查当加入中间结点后两顶点之间的最短距离是否发生变化,D数组中时刻存放两个结点的最短距离
/*----------弗洛伊德算法代码实现----------*/
void ShortestPath_Floyd(AMGraph G) {
	//用Floyd算法求有向图G中各顶点i和j之间的最短路径
	for(int i=0;i<G.vexnum;i++)//各对结点之间初始已知路径及距离
		for (int j = 0; j < G.vexnum; j++) {
			D[i][j] = G.arcs[i][j];
			if (D[i][j] < MaxInt && i != j) Path[i][j] = i;//如果i和j之间有弧,则将j的前驱置为1
			else Path[i][j] = -1;//如果i和j之间无弧,则将j的前驱置为-1
		}
	for(int k=0;k<G.vexnum;k++)
		for(int i=0;i<G.vexnum;i++)0
			for(int j=0;j<G.vexnum;j++)
				if (D[i][k] + D[k][j] < D[i][j]) {
					D[i][j] = D[i][k] + D[k][j];//更新D[i][j]
					Path[i][j] = Path[k][j];//更改j的前驱为k
				}
}

③测试的完整代码

测试的完整代码
最短路径基于图的邻接矩阵或邻接表的知识,由于此内容笔者已经在之前的文章中介绍过,这里不再过多赘述,对该内容不熟悉的读者欢迎移步此文章,共同学习!:数据结构C++——图的邻接矩阵和邻接表.

#include<iostream>
using namespace std;
/*-------图的邻接矩阵存储表示-----*/
#define MVNum 100//最大顶点数
#define MaxInt 32767//无穷大
#define OK 1
#define ERROR 0
typedef int Status;
typedef string VerTexType;//假设顶点的数据类型为字符型
typedef int ArcType;//假设边的权值类型为整型

bool visited[MVNum];//定义标志数组

typedef struct {
	VerTexType vexs[MVNum];//顶点表
	ArcType arcs[MVNum][MVNum];//邻接矩阵
	int vexnum, arcnum;//图的当前点数和边数
}AMGraph;
/*-----------迪杰斯特拉算法辅助数组的建立----------*/
bool S[MVNum];
int Path[MVNum][MVNum];
int D[MVNum][MVNum];
/*---------确定某顶点在G中的位置下标-----------*/
int LocateVex(AMGraph G, VerTexType u) {
	int i;
	for (int i = 0; i < G.vexnum; i++)
		if (u == G.vexs[i]) return i;
	return -1;
}
/*---------采用邻接矩阵表示法创建无向网-------*/
void CreateUDN(AMGraph& G) {
	//采用邻接矩阵表示法,创建无向网
	int i = 0, j = 0, k = 0;
	cin >> G.vexnum >> G.arcnum;//输入总顶点数,总边数
	for (i = 0; i < G.vexnum; i++)//依次输入点的信息
		cin >> G.vexs[i];
	for (i = 0; i < G.vexnum; i++)//初始化邻接矩阵
		for (j = 0; j < G.vexnum; j++)
			G.arcs[i][j] = MaxInt;
	for (k = 0; k < G.arcnum; k++) {
		VerTexType v1, v2;//定义两个顶点v1,v2
		int w = 0;
		cin >> v1 >> v2 >> w;
		i = LocateVex(G, v1);
		j = LocateVex(G, v2);//确定v1和v2在G中的位置,即顶点数组的下标
		G.arcs[i][j] = w;
	}
}
/*----------弗洛伊德算法代码实现----------*/
void ShortestPath_Floyd(AMGraph G) {
	//用Floyd算法求有向图G中各顶点i和j之间的最短路径
	for(int i=0;i<G.vexnum;i++)//各对结点之间初始已知路径及距离
		for (int j = 0; j < G.vexnum; j++) {
			D[i][j] = G.arcs[i][j];
			if (D[i][j] < MaxInt && i != j) Path[i][j] = i;//如果i和j之间有弧,则将j的前驱置为1
			else Path[i][j] = -1;//如果i和j之间无弧,则将j的前驱置为-1
		}
	for(int k=0;k<G.vexnum;k++)
		for(int i=0;i<G.vexnum;i++)
			for(int j=0;j<G.vexnum;j++)
				if (D[i][k] + D[k][j] < D[i][j]) {
					D[i][j] = D[i][k] + D[k][j];//更新D[i][j]
					Path[i][j] = Path[k][j];//更改j的前驱为k
				}
}
/*-----------输出结果-----------*/
void PrintResult(AMGraph G) {
	for (int i = 0; i < G.vexnum; i++) {
		for (int j = 0; j < G.vexnum; j++) {
			if (D[i][j] == MaxInt && i != j) {
				cout << "-1 ";
			}
			else if (i == j)
				cout << "0 ";
			else
				cout << D[i][j] << " ";
		}
		cout << endl;
	}
}
/*-----------主函数-----------*/
int main() {
	AMGraph G;
	CreateUDN(G);
	ShortestPath_Floyd(G);
	PrintResult(G);
	return 0;
}
输入:
4 8
v0 v1 v2 v3
v0 v1 1
v0 v3 4
v1 v2 9
v1 v3 2
v3 v2 6
v2 v0 3
v2 v1 5
v2 v3 8

输入数据构成的有向图:
在这里插入图片描述

输出:
0 1 9 3
11 0 8 2
3 4 0 6
9 10 6 0

三、总结

以上为笔者对于最短路径之DiJkstra算法和Floyd算法的一些见解,希望初学者都能有所收获,有技术不到位的地方,还望各位大佬指正。
同时,笔者的个人主页还有数据结构中其他部分的一些见解与分析,后续数据结构的相关知识还将陆续更新,欢迎大家访问且共同学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

近景_

打赏拉满,动力满满

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

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

打赏作者

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

抵扣说明:

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

余额充值