数据结构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算法的一些见解,希望初学者都能有所收获,有技术不到位的地方,还望各位大佬指正。
同时,笔者的个人主页还有数据结构中其他部分的一些见解与分析,后续数据结构的相关知识还将陆续更新,欢迎大家访问且共同学习