数据结构第三次实验-图及其应用

一、实验目的

掌握图的存储、构建、搜索等操作和应用,能用最短路径及其搜索等算法编制较
综合性的程序,求解最优路线问题,进行程序设计、数据结构和算法设计等方面的综合训练。

二、实验内容及要求

1、任务描述
实验内容:
用户驾车出行由于出行目的的不同对道路路线选择的要求也有不同。例如,有的希望
在途中的路程尽可能短,有的则可能希望路程中时间最短。为了能满足广大旅客的需求,
编制一个城市交通咨询模拟系统,选取城市部分位置、道路抽象为程序所需要图的顶点和
边,并以城市道路长度(路程),道路的某时段的速度等信息作为图结点中的弧信息,为旅
客提供这两种最优决策的交通咨询。
输入和输出:
(1)输入形式:
构建图时,输入顶点、弧涉及的信息,包括:起始地、目的地、长度、该弧此时间
段的平均速度等信息;
用户或者客户要输入出发地和目的地,并选择何种最优决策的路线规划。
(2)输出形式:根据用户需求输出对应信息
输出最短路程所需要的路线信息和最短路程;
输出最短时间所需要的路线信息和最短时间。
实验要求:
实现一个简单的交互式界面,包括系统菜单、清晰的输入提示等。
根据输入的交通图数据,以图形化形式把交通图显示在屏幕上。
以图形化形式把最优路线显示在屏幕上。

2、能够上机编辑、调试出完整的程序。主要数据类型与变量

typedef struct
{
	int vexs[MAXVEX];
	double arc[MAXVEX][MAXVEX];
	int numVertexes, numEdges;
}MGraph;

该结构体为图的存储结构,其中vexs数组存储顶点的下标,arc数组为该图的邻接矩阵,其中的数值为权值,即路径的长度或经过该路径的平均时间。Numvertexes为图中顶点的个数,numedges为图中边的条数。

typedef int Patharc[MAXVEX];

用于存储最短路径或最小时间下标的数组。

typedef int ShortPathTable[MAXVEX];

用于存储到各点最短路径或最小时间的权值和。
3、算法或程序模块

1.void CreateMGraph(MGraph* G)

该函数用来根据输入的顶点数和边数,对图进行初始化。对vexs数组进行对应的赋值,并且对邻接矩阵除起点和终点相同的点外全部赋值为INFINITY,即65535不可达。

2.void Init(MGraph *G,MGraph *T)

该函数用来根据用户输入的信息完成相关的赋值,根据起点和终点选择对应对边,然后按照输入的长度和速度,将长度赋给图M相应的边;将时间进行计算后赋给图T相应的边,并将其输出。

3.void ShortestPath_Dijkstra(MGraph G, int v0, Patharc* P, ShortPathTable* D)

Dijkstra算法的实现,该算法的首先将各个数组进行初始化,其中final[i]=1表示顶点Vi已经加入最短路径,P[v]的值为前驱顶点下标,D[v]表示v0到v的最短路径长度和。先将V0结点加入最短路径,接着通过循环每次求得v0到其它顶点的最短路径,在循环中先将最近的结点加入到最短路径,之后根据该结点相邻的边对数组D进行修改,得到最新的V0到各顶点的最短距离。

4.Main函数

根据用户输入的起点和终点信息,通过遍历存储最短路径顶点下标的数组P依次输出对应的点,通过存储源点到各顶点最小距离的数组D中输出对应的权值。

输出最少时间路径的代码结构与上面相同,不再赘述。

三、测试

1、方案
在这里插入图片描述

根据该图创建对应的图结构,输入顶点的个数和边数,以及对应边的长度和通过的速度(自定)。
测试数据如下:
在这里插入图片描述

2、结果

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

可以看出最短路径与测试图中的一致,即从顶点V0到V8依次需要经过V1、V2、V4、V3、V6、V7。最少时间的实现与此相同,只是权值不同,因此也应该是正确的。

四、总结与讨论

通过本次实验我进一步掌握了图的存储结构之一——邻接矩阵存储表示,以及熟悉了Dijkstra算法执行的具体流程。
因为本次实验需要两个权值用来存储图的相关信息,因此使用邻接表的存储其实是更为合适的,对于多权值只要在其边表结点结构中添加相应的变量即可。但还是实力有限,对于邻接表的操作还不太熟练因此也未采用邻接表的存储结构。对于邻接矩阵的存储表示,两个权值究竟如何保存我先前的尝试的做法是直接在这唯一的图的结构中再添加一个二维数组。这样存储方法确实可以,但在Dijkstra算法中实现时需要对相同的流程执行两次,考虑代码冗余量太大,因此又想到了可以共用这唯一的图结构,只需将数组arc的类型更改为double,这样也兼容了int型的数据,在主函数中创建两个该图的变量G、T,将第一个图G初始化后,直接用G将T覆盖,如此也省去了对T初始化的步骤。接着将这个两个图变量的地址传入函数Init即可实现对这两个图的权值赋值工作。最后再对得到的存储图相关信息的数组进行输出即可。
对于Dijkstra这类算法,我认为最重要的还是能够掌握其思想,然后能够对代码进行灵活的运用。如果想把算法的具体代码以及细节全部掌握难度很大,同时也需要花费很多的时间,因此以后还是要尽量配合一定实操以此更好地理解其思路,及时发现自己平时学习的不足和薄弱环节,从而加以弥补。

附:程序的源代码

#include "stdio.h"    
#include "stdlib.h"   
#include "io.h"  
#include "math.h"  
#include "time.h"

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

#define MAXEDGE 20
#define MAXVEX 20
#define GRAPH_INFINITY 65535

typedef int Status;	/* Status是函数的类型,其值是函数结果状态代码,如OK等 */

typedef struct
{
	int vexs[MAXVEX];
	double arc[MAXVEX][MAXVEX];
	int numVertexes, numEdges;
}MGraph;

typedef int Patharc[MAXVEX];    /* 用于存储最短路径下标的数组 */
typedef int ShortPathTable[MAXVEX];/* 用于存储到各点最短路径的权值和 */
/* 构件图 */
void CreateMGraph(MGraph* G)
{
	int i, j;
	//G->numEdges = MAXEDGE;
	//G->numVertexes = MAXVEX;
	printf("请输入边数和顶点数:"); 
	scanf("%d%d", &G->numEdges,&G->numVertexes);

	for (i = 0; i < G->numVertexes; i++)/* 初始化图 */
	{
		G->vexs[i] = i;
	}

	for (i = 0; i < G->numVertexes; i++)/* 初始化图 */
	{
		for (j = 0; j < G->numVertexes; j++)
		{
			if (i == j)
			{
				G->arc[i][j] = 0;
			}
			
			else
			{
				G->arc[i][j] = G->arc[j][i] = GRAPH_INFINITY;
			}
			
		}
	}

	//G->arc[0][1] = 1;
	//G->arc[0][2] = 5;
	//G->arc[1][2] = 3;
	//G->arc[1][3] = 7;
	//G->arc[1][4] = 5;

	//G->arc[2][4] = 1;
	//G->arc[2][5] = 7;
	//G->arc[3][4] = 2;
	//G->arc[3][6] = 3;
	//G->arc[4][5] = 3;

	//G->arc[4][6] = 6;
	//G->arc[4][7] = 9;
	//G->arc[5][7] = 5;
	//G->arc[6][7] = 2;
	//G->arc[6][8] = 7;

	//G->arc[7][8] = 4;


	//for (i = 0; i < G->numVertexes; i++)
	//{
	//	for (j = i; j < G->numVertexes; j++)
	//	{
	//		G->arc[j][i] = G->arc[i][j];
	//	}
	//}


}
void Init(MGraph *G,MGraph *T)
{
	printf("\n请输入每条路的起点、终点、长度、速度\n(以Ctrl+Z结束输入):\n");
	int i, j, length, speed;

	while (scanf("%d %d %d %d", &i, &j, &length, &speed) != EOF)
	{//开始地,目标地,距离,速度
		G->arc[i][j] = length;
		G->arc[j][i] = length;
		T->arc[i][j] = (1.0*length) / speed;
		T->arc[i][j] = (1.0*length) / speed;
		printf("经过弧(V%d-V%d)的平均时间为:%.2f\n\n",i,j, T->arc[i][j]);
	}


}

/*  Dijkstra算法,求有向网G的v0顶点到其余顶点v的最短路径P[v]及带权长度D[v] */
/*  P[v]的值为前驱顶点下标,D[v]表示v0到v的最短路径长度和 */
void ShortestPath_Dijkstra(MGraph G, int v0, Patharc* P, ShortPathTable* D)
{
	int v, w, k, min;
	int final[MAXVEX];/* final[w]=1表示求得顶点v0至vw的最短路径 */
	for (v = 0; v < G.numVertexes; v++)    /* 初始化数据 */
	{
		final[v] = 0;			/* 全部顶点初始化为未知最短路径状态 */
		(*D)[v] = G.arc[v0][v];/* 将与v0点有连线的顶点加上权值 */
		(*P)[v] = -1;				/* 初始化路径数组P为-1  */
	}

	(*D)[v0] = 0;  /* v0至v0路径为0 */
	final[v0] = 1;    /* v0至v0不需要求路径 */
	/* 开始主循环,每次求得v0到某个v顶点的最短路径 */
	for (v = 1; v < G.numVertexes; v++)
	{
		min = GRAPH_INFINITY;    /* 当前所知离v0顶点的最近距离 */
		for (w = 0; w < G.numVertexes; w++) /* 寻找离v0最近的顶点 */
		{
			if (!final[w] && (*D)[w] < min)
			{
				k = w;
				min = (*D)[w];    /* w顶点离v0顶点更近 */
			}
		}
		final[k] = 1;    /* 将目前找到的最近的顶点置为1 */
		for (w = 0; w < G.numVertexes; w++) /* 修正当前最短路径及距离 */
		{
			/* 如果经过v顶点的路径比现在这条路径的长度短的话 */
			if (!final[w] && (min + G.arc[k][w] < (*D)[w]))
			{ /*  说明找到了更短的路径,修改D[w]和P[w] */
				(*D)[w] = min + G.arc[k][w];  /* 修改当前路径长度 */
				(*P)[w] = k;
			}
		}
	}
}


int main(void)
{
	int i, j, v0,Vn;
	MGraph G,T;
	Patharc P,P2;
	ShortPathTable D,D2; /* 求某点到其余各点的最短路径 */
	v0 = 0;

	CreateMGraph(&G);
	T = G;
	Init(&G,&T);
	printf("\n请输入所查询路径的起点、终点:");
	scanf("%d%d", &v0, &Vn);

	ShortestPath_Dijkstra(G, v0, &P, &D);
	printf("最短路径如下:\n");

	printf("v%d - v%d : ", v0, Vn);
	j = Vn;
	printf("v%d<-", Vn);
	while (P[j] != -1)
	{
		printf("V%d<-", P[j]);
		j = P[j];
	}
	printf("v%d", v0);
	printf("\n");
	
	printf("\n源点到终点的最短路径长度为:\n");
	printf("v%d - v%d : %d \n", v0, G.vexs[Vn], D[Vn]);



	printf("---------------------------\n");
	ShortestPath_Dijkstra(T, v0, &P2, &D2);

	printf("最少时间如下:\n");
	printf("v%d - v%d : ", v0, Vn);
	j = Vn;
	printf("v%d<-", Vn);
	while (P2[j] != -1)
	{
		printf("V%d<-", P2[j]);
		j = P2[j];
	}
	printf("v%d", v0);
	printf("\n");

	printf("\n源点到终点的最少时间为:\n");
	printf("v%d - v%d : %d \n", v0, T.vexs[Vn], D2[Vn]);
	return 0;
}
  • 7
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ZZULI丶TiAmo

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值