1. 实验目的
(1)理解图的邻接矩阵或邻接表表示方法,能将图抽象为合适的存储模型;
(2)能使用最短路径Dijkstra算法解决校园导航问题。
2. 实验内容
(1)第一关:能实现图的邻接矩阵存储;
(2)第二关:能输入地图,设计实现最短路径Dijkstra算法。
1、数据结构设计
图的存储:用邻接矩阵,这样会方便不少。
邻接矩阵是一个二维数组,数组中的元素是边的权(一些数值),数组下标号为结点的标号。
(1)例如二维数组中的一个元素M[5][6]的值为39,则表示结点5、6连接,且其上的权值为39。
(2)用邻接矩阵存储图,对图的读写就简单了。因为邻接矩阵就是一个二维数组,因此对图的读写就是对二维数组的操作。只要能弄清楚边的编号,就能把图读入了。
用一对结点表示边(也就是输入的时候输入一对结点的编号)
2、实验要求算法
1.Dijkstra生成最短路径
书上给出了完整的求最短路径的源代码。总的来说的不断添加点的过程,把距离原点路径最短的点添加(通过做标记的方法)并更新没有做标记的图中点距离原点的距离,直到所有的点都做了标记。
自己编写代码的时候注意,书上仅仅是给出了最短路径的源代码,离一个完整的程序还有距离,其中要补充图的读写的函数和定义一些常量。关键是要明白书上代码的意思。
求最短路径的Dijkstra算法:
求最短路径就是求图中的每一个点到图中某一个给定点(这里认为是编号为0的点)的最短距离。
具体算法就是初始有一个旧图,一个新图。开始的时候旧图中有所有的结点,新图中初始为只有一个结点(源点,路径的源头)。整个算法就是不停的从旧图中往新图中添加点,直到所有的点都添加到新图中为止。
要实现这个算法,除了用二维数组保存图,还需要使用到两个辅助的数组
数组find[N]:此数组是用来表示标号对应的结点是否已经被添加到新图中(因为只有旧图中的点我们才需要添加到新图中,并且只有旧图中点到源点的距离,我们才需要进行更新)其中N为图中结点的个数。
数组distance[N]:此数组记录图中的点到源点的距离。这个数组里面存放的值是不断进行更新的。其中N为图中结点的个数。
大的循环:逐一把旧图中的所有点添加到新图中。循环:遍历distance数组,找distance数组中属于旧图中的点,其中distance最小的那个结点循环:遍历distance数组,找distance数组中属于旧图中的点,其中distance最小的那个结点。更新distance数组,因为新添加了一个点到新图中,所以可能能找到一条通过这条点的新的路径,这条路径可能比原来的路径更短。
下面的测试用图见上面的图片中的图。 测试输入(前面9行都是构造图需要的起点 终点 权值,最后一行是须计算的路径的起点和终点):
1 2 1
1 3 12
2 3 9
2 4 3
4 3 4
4 5 13
3 5 5
4 6 15
5 6 4
0 6
预期输出(输出的是0号点到6号点的最短路径长度):
17
测试输入:
1 2 1
1 3 12
2 3 9
2 4 3
4 3 4
4 5 13
3 5 5
4 6 15
5 6 4
0 3
预期输出: 8
#include <stdio.h>
#include <stdlib.h>
#define MAXVEX 100 // 定义最大顶点数
#define INFINITY 65535 // 定义无穷大
typedef struct {
int vexs[MAXVEX]; // 顶点数组
int arc[MAXVEX][MAXVEX]; // 邻接矩阵
int numVertexes, numEdges;//顶点数,边数
} MGraph; // 定义图的结构体
void CreateMGraph(MGraph *G) {
int i, j, k, w;
FILE *fp;
fp = fopen("data.txt", "r"); // 打开文件
fscanf(fp, "%d %d", &G->numVertexes, &G->numEdges); // 读取顶点数和边数
// 初始化邻接矩阵
for (i = 0; i < G->numVertexes; i++)
{
for (j = 0; j < G->numVertexes; j++)
{
G->arc[i][j] = INFINITY; // 将所有元素设为无穷大
}
}
// 读取边的信息并存储到邻接矩阵中
for (k = 0; k < G->numEdges; k++) {
fscanf(fp, "%d %d %d", &i, &j, &w); // 读取一条边的起点,终点和权值
G->arc[i][j] = w; // 将对应位置的元素设为权值
}
}
void Dijkstra(MGraph G, int v0, int *dist) {
int i, j, k;
int min;
int final[MAXVEX];//记录结点时是否被访问过,访问过为1,没有访问过为0
// dist[]记录每个从v0结点开始到其他各个结点的长度(权值)
// 初始化
for (i = 0; i < G.numVertexes; i++)
{
final[i] = 0;//目前每个结点均未被访问过,设为0
dist[i] = G.arc[v0][i]; // dist[]初始化为v0到各个顶点的距离(权值)
}
final[v0] = 1; //从v0开始访问
dist[v0] = 0;//v0到v0的路径为0
//开始主循环,每次求得v0到某个顶点的最短路径
for (i = 1; i < G.numVertexes; i++)
{
min = INFINITY;//声明一个min=INFINITY用来每次记录这次遍历找到的最短路径的长度(权值)
for (j = 0; j < G.numVertexes; j++)
{
if (final[j]!=1 && dist[j]<min)//从v0到结点j的路径如果小于这次遍历找到的最短路径长度(权值)
{
min = dist[j];
k = j;
}//找出v0到结点j的最短路径,并且记录下最短路径的结点k=j
}
final[k] = 1;//找到结点k,即已访问过k,final[k]=1
//更新dist数组
for (j = 0; j < G.numVertexes; j++)
{
if (final[j]!=1 && dist[k] + G.arc[k][j] < dist[j])
{
dist[j] =dist[k] + G.arc[k][j];
}//如果v→k→j的路径比v→j的路径短,那么修改dist[j]的值为 dist[k]+G.arc[k][j]
}
}
//数组dist[]就是存放了起点v0开始到各个顶点的最短路径长度
}
void PrintMGraph(MGraph G) {
int i, j;
for (i = 0; i < G.numVertexes; i++)
{
for (j = 0; j < G.numVertexes; j++)
{
printf("%d ", G.arc[i][j]); // 打印邻接矩阵的元素
}
printf("\n"); //换行
}
}
int main() {
MGraph G;
CreateMGraph(&G); //创建图
int v0 = 0;
int dist[MAXVEX];
Dijkstra(G, v0, dist); // 调用Dijkstra算法求最短路径
printf("v%d到各个顶点的最短距离为:\n", v0);
for (int i = 1; i < G.numVertexes; i++) {
printf("v%d -> v%d : %d\n", v0, i, dist[i]); //打印结果
}
system("pause"); //暂停程序,等待用户输入
return 0;
}