哈尔滨工业大学计算学部
实验报告
课程名称:数据结构与算法
实验项目:图形结构及其应用
实验题目:最短路径算法
- 实验目的
最短路径问题研究的主要有:单源最短路径问题和所有顶点对之间的最短路径问题。在计算机领域和实际工程中具有广泛的应用,如集成电路设计、GPS/ 游戏地图导航、智能交通、路由选择、铺设管线等。本实验要求设计和实现 Dijkstra 算法和 Floyd-Warshall 算法,求解最短路径问题
二、实验要求及实验环境
实验要求:
1. 实现单源最短路径的 Dijkstra 算法,输出源点及其到其他顶点的最短路径长度和最短路径。
2. 实现全局最短路径的 Floyd-Warshall算法。计算任意两个顶点间的最短距离矩阵和最短路径矩阵,并输出任意两个顶点间的最短路径长度和最短路径。
3. 利用 Dijkstra 或 Floyd-Warshall 算法解决单目标最短路径问题:找出图中每个顶点 v 到某个指定顶点 c 最短路径。
4. 利用 Dijkstra 或 Floyd-Warshall算法解决单顶点对间最短路径问题:对于某对顶点 v 和 u,找出 u 到 v 和 v 到 u 的一条最短路径。
5. 以文件形式输入图的顶点和边,并以适当的方式展示相应的结果。要求顶点
不少于 10 个,边不少于 13 个。
实验环境:
我本次使用Visual Studio Code来编写C++程序。
三、设计思想(本程序中的用到的所有数据类型的定义,主程序的流程图及各程序模块之间的调用关系、核心算法的主要步骤)
1.所使用的数据结构:
(1)定义了MTGraph结构体来存储图结构,其成员有vertex一维数组用于存储图的顶点序列,edge二维数组用于存储边的权值,n用于存储顶点数,e用于存储边数。
(2)D一维数组,用于存储源点到各个顶点的最短路径长度
(3)S一维数组,用于存储顶点是否已经求出最短路径
(4)P一维数组,用于存储最短路径的前一个结点
(5)Length二维数组,用于存储任意两个顶点间的最短路径
(6)Road二维数组,用于最短路径编号
- 算法设计逻辑及程序运行效果:
(1)int MinCost(MTGraph *G)
该函数用于计算当前最短路径下标,temp为所有未确定最短路径中最短的路径,初始化temp为min即两个顶点之间没有边,可认为为无穷大,初始化w为1,循环除源点外的各个顶点,如果该顶点未确定最短路径,且其当前最短路径值小于temp,则更新temp为当前最短路径值,循环结束后,得到temp为最短路径,w为最短路径下标。
(2)void CreateMGraph(MTGraph *G)
该函数用于从文件中读取创建图,下面为文件输入信息,依据输入格式读取顶点数,边数,顶点序列及边的权值即可。
10 18
0 1 2 3 4 5 6 7 8 9
0 1 4
1 8 3
8 9 2
1 7 3
2 5 9
2 7 5
3 4 4
3 9 10
4 7 15
4 6 12
5 1 7
5 3 9
5 8 12
6 8 4
6 2 1
7 3 2
8 9 14
7 6 14
(3)void ShowMTGraph(MTGraph *G)
该函数用于输出从文件中读入的数据,遍历存储顶点序列的vertex矩阵及存储边信息的edge信息矩阵即可。下面为控制台输出信息。
the dots list:
0123456789
the adjacency matrix:
99 4 99 99 99 99 99 99 99 99
99 99 99 99 99 99 99 3 3 99
99 99 99 99 99 9 99 5 99 99
99 99 99 99 4 99 99 99 99 10
99 99 99 99 99 99 12 15 99 99
99 7 99 9 99 99 99 99 12 99
99 99 1 99 99 99 99 99 4 99
99 99 99 2 99 99 14 99 99 99
99 99 99 99 99 99 14 99 99 14
99 99 99 99 99 99 14 99 99 99
(4)void Dijkstra(MTGraph *G)
该函数用于求源点0到每个顶点的最短路径,先初始化D,P,S矩阵,之后循环遍历除源点外的所有点,获取到所有未确定最短路径中最短路径的下标,再嵌套循环遍历除源点外的所有点,如果其最短路径未确定,则计算已确定的w的最短路径加上w与j之间边的长度,如果其小于原来的值,则更换,且找到j的最短路径。打印出如下输出。
0->1
distance:4
path:0->1
0->2
distance:22
path:0->1->7->6->2
0->3
distance:9
path:0->1->7->3
0->4
distance:13
path:0->1->7->3->4
0->5
distance:31
path:0->1->7->6->2->5
0->6
distance:21
path:0->1->7->6
0->7
distance:7
path:0->1->7
0->8
distance:7
path:0->1->8
0->9
distance:19
path:0->1->7->3->9
(5)void Floyd(MTGraph *G)
该函数用于通过Floyd算法求任意两个顶点之间的最短路径,先初始化Length及Road矩阵,之后通过三层循环确定最短路径长度及其前驱结点。并打印出如下输出。
最短距离矩阵
inf 4 22 9 13 31 21 7 7 19
inf 34 18 5 9 27 17 3 3 15
inf 16 20 7 11 9 19 5 19 17
inf 33 17 21 4 26 16 19 20 10
inf 29 13 17 21 22 12 15 16 27
inf 7 25 9 13 34 24 10 10 19
inf 17 1 8 12 10 18 6 4 18
inf 31 15 2 6 24 14 20 18 12
inf 31 15 22 26 24 14 20 18 14
inf 31 15 22 26 24 14 20 18 32
最短路径矩阵
-1 -1 7 7 7 7 7 1 1 7
-1 7 7 7 7 7 7 -1 -1 7
-1 5 7 7 7 -1 7 -1 5 7
-1 6 6 7 -1 6 4 4 6 -1
-1 6 6 7 7 6 -1 -1 6 7
-1 -1 7 -1 3 7 7 1 1 3
-1 5 -1 7 7 2 8 2 -1 7
-1 6 6 -1 3 6 -1 6 6 3
-1 6 6 7 7 6 -1 6 6 -1
-1 6 6 7 7 6 -1 6 6 7
路径及长度
0->0: no way
0->1: distance:4 path:0->1
0->2: distance:22 path:0->1->7->6->2
0->3: distance:9 path:0->1->7->3
0->4: distance:13 path:0->1->7->3->4
0->5: distance:31 path:0->1->7->6->2->5
0->6: distance:21 path:0->1->7->6
0->7: distance:7 path:0->1->7
0->8: distance:7 path:0->1->8
0->9: distance:19 path:0->1->7->3->9
1->0: no way
1->1: distance:34 path:1->7->6->2->5->1
1->2: distance:18 path:1->7->6->2
1->3: distance:5 path:1->7->3
1->4: distance:9 path:1->7->3->4
1->5: distance:27 path:1->7->6->2->5
1->6: distance:17 path:1->7->6
1->7: distance:3 path:1->7
1->8: distance:3 path:1->8
1->9: distance:15 path:1->7->3->9
2->0: no way
2->1: distance:16 path:2->5->1
2->2: distance:20 path:2->7->6->2
2->3: distance:7 path:2->7->3
2->4: distance:11 path:2->7->3->4
2->5: distance:9 path:2->5
2->6: distance:19 path:2->7->6
2->7: distance:5 path:2->7
2->8: distance:19 path:2->5->1->8
2->9: distance:17 path:2->7->3->9
3->0: no way
3->1: distance:33 path:3->4->6->2->5->1
3->2: distance:17 path:3->4->6->2
3->3: distance:21 path:3->4->7->3
3->4: distance:4 path:3->4
3->5: distance:26 path:3->4->6->2->5
3->6: distance:16 path:3->4->6
3->7: distance:19 path:3->4->7
3->8: distance:20 path:3->4->6->8
3->9: distance:10 path:3->9
4->0: no way
4->1: distance:29 path:4->6->2->5->1
4->2: distance:13 path:4->6->2
4->3: distance:17 path:4->7->3
4->4: distance:21 path:4->7->3->4
4->5: distance:22 path:4->6->2->5
4->6: distance:12 path:4->6
4->7: distance:15 path:4->7
4->8: distance:16 path:4->6->8
4->9: distance:27 path:4->7->3->9
5->0: no way
5->1: distance:7 path:5->1
5->2: distance:25 path:5->1->7->6->2
5->3: distance:9 path:5->3
5->4: distance:13 path:5->3->4
5->5: distance:34 path:5->1->7->6->2->5
5->6: distance:24 path:5->1->7->6
5->7: distance:10 path:5->1->7
5->8: distance:10 path:5->1->8
5->9: distance:19 path:5->3->9
6->0: no way
6->1: distance:17 path:6->2->5->1
6->2: distance:1 path:6->2
6->3: distance:8 path:6->2->7->3
6->4: distance:12 path:6->2->7->3->4
6->5: distance:10 path:6->2->5
6->6: distance:18 path:6->8->6
6->7: distance:6 path:6->2->7
6->8: distance:4 path:6->8
6->9: distance:18 path:6->2->7->3->9
7->0: no way
7->1: distance:31 path:7->6->2->5->1
7->2: distance:15 path:7->6->2
7->3: distance:2 path:7->3
7->4: distance:6 path:7->3->4
7->5: distance:24 path:7->6->2->5
7->6: distance:14 path:7->6
7->7: distance:20 path:7->6->2->7
7->8: distance:18 path:7->6->8
7->9: distance:12 path:7->3->9
8->0: no way
8->1: distance:31 path:8->6->2->5->1
8->2: distance:15 path:8->6->2
8->3: distance:22 path:8->6->2->7->3
8->4: distance:26 path:8->6->2->7->3->4
8->5: distance:24 path:8->6->2->5
8->6: distance:14 path:8->6
8->7: distance:20 path:8->6->2->7
8->8: distance:18 path:8->6->8
8->9: distance:14 path:8->9
9->0: no way
9->1: distance:31 path:9->6->2->5->1
9->2: distance:15 path:9->6->2
9->3: distance:22 path:9->6->2->7->3
9->4: distance:26 path:9->6->2->7->3->4
9->5: distance:24 path:9->6->2->5
9->6: distance:14 path:9->6
9->7: distance:20 path:9->6->2->7
9->8: distance:18 path:9->6->8
9->9: distance:32 path:9->6->2->7->3->9
(6)void Single_Target_Floyd(MTGraph *G, int v)
该函数用于求解单目标最短路径,利用前面写过的Folyd算法易求解此问题,调用Folyd算法后,只需按照需要输出即可。下面为输出结果。
0->4: distance:13 path:0->1->7->3->4
1->4: distance:9 path:1->7->3->4
2->4: distance:11 path:2->7->3->4
3->4: distance:4 path:3->4
4->4: distance:21 path:4->7->3->4
5->4: distance:13 path:5->3->4
6->4: distance:12 path:6->2->7->3->4
7->4: distance:6 path:7->3->4
8->4: distance:26 path:8->6->2->7->3->4
9->4: distance:26 path:9->6->2->7->3->4
(7)void Single_Source_Floyd(MTGraph *G, int u, int v)
该函数用于求单顶点对间最短路径,,利用前面写过的Floyd算法易求解此问题,调用Floyd算法后,只需按照需要输出即可。下面为输出结果。
3->6: distance:16 path:3->4->6
6->3: distance:8 path:6->2->7->3
四、测试结果
已在第三部分结合函数介绍给出运行结果。
五、经验体会与不足
通过此次实验的练习我更加熟悉栈的操作,并且发现了之前学习栈上的漏洞,并且由于程序代码较多,存在“牵一发而动全身”的情况,我认识到先有一个整体架构再填充细节的重要性,每修改一个地方都应注意是否会影响程序的其它部分,以下简述我在实验中遇到的问题。
- 对Dijkstra算法和Floyd算法的原理还有一些不清晰的地方,通过观看网课视频及老师讲解解决。
- 输出最短路径时没有初始化栈导致结果出错,与同学交流后发现该问题。
#include <iostream>
using namespace std;
#define MAX 20
const int MIN = 99;
int D[MAX]; //源点到顶点的当前最短距离
int P[MAX]; //源点到顶点的当前最短路径最后经过的顶点
bool S[MAX]; //判断顶点是否求出最短路径
int Length[MAX][MAX]; //最短路径长度
int Road[MAX][MAX]; //最短路径编号
typedef int VertexData;
typedef int EdgeData;
typedef struct
{
VertexData vertex[MAX]; //顶点表
EdgeData edge[MAX][MAX]; //邻接矩阵
int n, e; //图的顶点数和边数
} MTGraph; //邻接矩阵结构体
void Dijkstra(MTGraph *G); //源点0到其他顶点的最短路径
int MinCost(MTGraph *G); //计算当前最短路径的下标
void CreateMGraph(MTGraph *G);
void ShowMTGraph(MTGraph *G);
void Floyd(MTGraph *G);
void Path(FILE *fp, int i, int j);
void Single_Target_Floyd(MTGraph *G, int v); //单目标最短路径
void Single_Source_Floyd(MTGraph *G, int u, int v); //单顶点对最短路径
int main()
{
MTGraph G;
CreateMGraph(&G);
ShowMTGraph(&G);
Dijkstra(&G);
Floyd(&G);
Single_Target_Floyd(&G, 4);
Single_Source_Floyd(&G, 3, 6);
return 0;
}
void Dijkstra(MTGraph *G)
{
int w;
for (int i = 0; i < G->n; i++)
{
D[i] = G->edge[0][i];
S[i] = false;
}
S[0] = true;
// n-1次
for (int i = 1; i < G->n; i++)
{
w = MinCost(G);
S[w] = true;
for (int v = 1; v < G->n; v++)
{
if ((S[v] != true) && ((D[w] + G->edge[w][v]) < D[v]))
{
D[v] = D[w] + G->edge[w][v];
P[v] = w;
}
}
}
FILE *fp;
fp = fopen("Dijkstra.txt", "w");
//最短路径长度和最短路径
for (int i = 1; i < G->n; i++)
{
if (D[i] != MIN)
{
fprintf(fp, "0->%d\ndistance:%d\npath:0", i, D[i]);
int stack[MAX];
int top = 0;
stack[top] = i;
while (stack[top] != 0)
{
int temp = stack[top];
stack[++top] = P[temp];
}
top--;
while (top >= 0)
fprintf(fp, "->%d", stack[top--]);
fprintf(fp, "\n\n");
}
else
fprintf(fp, "0->%d:no way\n", i);
}
fclose(fp);
}
int MinCost(MTGraph *G)
{
int w = 1;
int temp = MIN;
for (int i = 1; i < G->n; i++)
{
if (S[i] != true && D[i] < temp)
{
temp = D[i];
w = i;
}
}
return w;
}
void CreateMGraph(MTGraph *G)
{
int i, j, w;
FILE *fp;
fp = fopen("input.txt", "r");
if (fp == NULL)
{
printf("error:can't open the file");
exit(0);
}
fscanf(fp, "%d %d", &G->n, &G->e);
for (int i = 0; i < G->n; i++)
for (int j = 0; j < G->n; j++)
G->edge[i][j] = MIN;
for (int i = 0; i < G->n; i++)
fscanf(fp, "%d", &G->vertex[i]);
for (int i = 0; i < G->e; i++)
{
fscanf(fp, "%d %d %d", &i, &j, &w);
G->edge[i][j] = w;
}
fclose(fp);
}
void ShowMTGraph(MTGraph *G)
{
cout << "the dots list:\n";
for (int i = 0; i < G->n; i++)
cout << G->vertex[i];
cout << endl
<< "the adjacency matrix:" << endl;
for (int i = 0; i < G->n; i++)
{
for (int j = 0; j < G->n; j++)
cout << G->edge[i][j] << ' ';
cout << endl;
}
}
void Floyd(MTGraph *G)
{
for (int i = 0; i < G->n; i++)
for (int j = 0; j < G->n; j++)
{
Length[i][j] = G->edge[i][j];
Road[i][j] = -1;
}
for (int k = 0; k < G->n; k++)
for (int i = 0; i < G->n; i++)
for (int j = 0; j < G->n; j++)
{
if (Length[i][j] > Length[i][k] + Length[k][j])
{
Length[i][j] = Length[i][k] + Length[k][j];
Road[i][j] = k;
}
}
FILE *fp;
fp = fopen("Floyd.txt", "w");
fclose(fp);
fp = fopen("Floyd.txt", "a");
//最短距离矩阵
fprintf(fp, "最短距离矩阵\n");
for (int i = 0; i < G->n; i++)
{
for (int j = 0; j < G->n; j++)
{
if (Length[i][j] != MIN)
fprintf(fp, "%d\t", Length[i][j]);
else
fprintf(fp, "inf\t");
}
fprintf(fp, "\n");
}
fprintf(fp, "\n");
//最短路径矩阵
fprintf(fp, "最短路径矩阵\n");
for (int i = 0; i < G->n; i++)
{
for (int j = 0; j < G->n; j++)
{
fprintf(fp, "%d\t", Road[i][j]);
}
fprintf(fp, "\n");
}
fprintf(fp, "\n");
//路径及长度
fprintf(fp, "路径及长度\n");
for (int i = 0; i < G->n; i++)
{
for (int j = 0; j < G->n; j++)
{
fprintf(fp, "%d->%d:\t", i, j);
if (Length[i][j] != MIN)
{
fprintf(fp, "distance:%d\t\tpath:", Length[i][j]);
fprintf(fp, "%d", i);
Path(fp, i, j);
fprintf(fp, "->%d\n", j);
}
else
{
fprintf(fp, "no way\n");
}
}
fprintf(fp, "\n");
}
fclose(fp);
}
void Path(FILE *fp, int i, int j)
{
int k = Road[i][j];
if (k != -1)
{
Path(fp, i, k);
fprintf(fp, "->%d", k);
Path(fp, k, j);
}
}
void Single_Target_Floyd(MTGraph *G, int v)
{
FILE *fp = fopen("Single_Target.txt", "w");
for (int i = 0; i < G->n; i++)
{
fprintf(fp, "%d->%d:\t", i, v);
if (Length[i][v] != MIN)
{
fprintf(fp, "distance:%d\t\tpath:", Length[i][v]);
fprintf(fp, "%d", i);
Path(fp, i, v);
fprintf(fp, "->%d\n", v);
}
else
{
fprintf(fp, "no way\n");
}
}
fclose(fp);
}
void Single_Source_Floyd(MTGraph *G, int u, int v)
{
FILE *fp = fopen("Single_Source.txt", "w");
fprintf(fp, "%d->%d:\t", u, v);
if (Length[u][v] != MIN)
{
fprintf(fp, "distance:%d\t\tpath:", Length[u][v]);
fprintf(fp, "%d", u);
Path(fp, u, v);
fprintf(fp, "->%d\n", v);
}
else
{
fprintf(fp, "no way\n");
}
fprintf(fp, "%d->%d:\t", v, u);
if (Length[v][u] != MIN)
{
fprintf(fp, "distance:%d\t\tpath:", Length[v][u]);
fprintf(fp, "%d", v);
Path(fp, v, u);
fprintf(fp, "->%d\n", u);
}
else
{
fprintf(fp, "no way\n");
}
}