数据结构实验2--景区信息系统管理

本文围绕景区景点信息管理系统展开,介绍了四个阶段的工作。包括创建和查询景点信息、利用邻接矩阵进行深度优先搜索实现导航、用Dijkstra方法找最短路径、用prim法构建最小生成树进行电路规划。实验运用图的知识,解决了一些小问题,加深了对相关知识的理解。

一、第一阶段:景区景点信息创建和景点信息查询

在这一阶段主要是建立数据结构,读取文本文件中的景点信息,并利用景点图的邻接矩阵完成景点信息搜索

搜索代码如下:

int CGraph::FindEdge(int nVex)//查询与指定顶点相连的边
{
	cout << CGraph::m_aVexs[nVex].name << endl;
	cout << CGraph::m_aVexs[nVex].desc << endl;
	cout << "**********周边景区**********" << endl;
	int k = 0;
	for (int i = 0; i < CGraph::m_nVexNum; i++)
	{
		if (CGraph::m_aAdjMatrix[nVex][i] != 0)
		{
			cout << CGraph::m_aVexs[nVex].name << "->" << CGraph::m_aVexs[i].name << "\t" << CGraph::m_aAdjMatrix[nVex][i] << endl;
			k++;
		}
	}
	return k;
}

二、利用邻接矩阵进行图的深度优先搜索,实现景点导航功能

由用户输入起点位置,输出所有路径(经过景区所有景点一次)。

深度优先遍历代码如下(递归方式):

void CGraph::DFS(int nVex, int visited[],int aPath[], int& nIndex,Path* Pathlist)
{
	visited[nVex] = 1;//将结点nVex状态改为已访问
	aPath[nIndex++] = nVex;
	if (nIndex == CGraph::m_nVexNum)//顶点遍历完毕,保存一条路径
	{
		Path* p = new Path;
		p->vexs = new int[CGraph::m_nVexNum];
		for (int i = 0; i < CGraph::m_nVexNum; i++)
			p->vexs[i] = aPath[i];
		Path* q = Pathlist;
		while (q->next != NULL)
			q = q->next;
		q->next = p;
	}
	else
	{
		for (int i = 0; i < CGraph::m_nVexNum; i++)
		{
			if (CGraph::m_aAdjMatrix[nVex][i] != 0 && visited[i] == 0)
			{
				CGraph::DFS(i, visited, aPath, nIndex,Pathlist);//递归遍历
				visited[i] = 0; 
				nIndex--;
			}
		}
	}
}

刚开始设置了一个尾指针Tail指向链表的尾部,插入结点选用尾插法,后来发现比较麻烦,然后每次都遍历找到链表最后一个结点,但是这样时间开销比较大,现在我想起来了头插法,我好蠢。。

三、第三阶段:搜索两个顶点之间的最短路径

采用Dijkstra方法查找最短路径,但是我之前没有学Dijkstra方法,在进行实验之前去学习了一下这个方法,学习到的大概如下:

1.将顶点集合划分为两部分:S和U。顶点S中保存已经找到到达起点最短路径的顶点,U中保存未找到最短路径的顶点。disc[i]表示顶点i到起点的最短路径长度,pre[i]表示顶点i到起点的最短路径中,i之前的顶点,flag[i]=0表示顶点i在集合U中。

2.从U中选择disc最小的顶点j,加入集合S中,并更新集合U中的顶点,即更新集合U中顶点的disc,直至选完所有顶点。

详细链接:https://blog.youkuaiyun.com/chen134225/article/details/79886928

但是在实验中,我遇到的困难是:当两个顶点之间没有边时,边的权是0,不好处理,而且,当选出的顶点与起点相连时,pre没有保存具体的值,造成错误,最终实验中采用在初始化时,如果顶点和起点相连,pre初始化为起点。

代码如下:

void CGraph::FindShortPath(int nVexStart, int nVexEnd,int &length)//查询两景点之间的最短路径
{
	length = 0;
	int *disc = new int[CGraph::m_nVexNum];
	int k;
	//pre[i]是顶点nVexStart到顶点i的最短路径中,i前面一个顶点,flag[i]为0,表示未找到从nVexStart到顶点i的最短路径
	//disc[i]表示从起点到顶点i的最短路径长度
	int * pre = new int[CGraph::m_nVexNum],*flag=new int[CGraph::m_nVexNum];
	for (int i = 0; i < CGraph::m_nVexNum; i++)
	{
		pre[i] = -1;
		if (CGraph::m_aAdjMatrix[nVexStart][i] != 0)
		{
			disc[i] = CGraph::m_aAdjMatrix[nVexStart][i];
			pre[i] = nVexStart;
		}
		else
			disc[i] = INT_MAX;
		flag[i] = 0;
		
	}
	flag[nVexStart] = 1;
	disc[nVexStart] = 0;
	for (int i = 0; i < CGraph::m_nVexNum-1; i++)//遍历顶点集合m_nVexNum-1次,每次找出一个顶点的最短路径,找到nVexEnd时跳出循环
	{
		int  min = INT_MAX;
		for (int j = 0; j < CGraph::m_nVexNum; j++)//在未找到最短路径的顶点中找出disc最小的顶点
		{
			if (flag[j] == 0 && disc[j] < min)
			{
				min = disc[j];
				k = j;
			}
		}
		flag[k] = 1;
		if (k == nVexEnd)//找到终点
			break;
		//修正剩下未找到最短路径的顶点
		for (int j = 0; j < CGraph::m_nVexNum; j++)
		{
			int temp;
			if (CGraph::m_aAdjMatrix[k][j] == 0)
				temp = INT_MAX;
			else
				temp = min + CGraph::m_aAdjMatrix[k][j];
			if (flag[j] == 0 && disc[j] > temp)
			{
				disc[j] = temp;
				pre[j] = k;
			}
		}
	}
	struct node//定义路径结点
	{
		int num;//景点编号
		node* next=NULL;//下一景点
	};
	node* head = new node,*p=NULL;
	int i = nVexEnd,j=i;
	while (i != nVexStart)
	{
		p = new node;
		p->num = i;
		p->next = head->next;
		head->next = p;
		i = pre[i];

	}
	p = new node;
	p->num = nVexStart;
	p->next = head->next;
	head->next = p;
	length = disc[nVexEnd];
	p = head->next;
	cout << "最短路线为:";
	while (p->next != NULL)
	{
		cout << CGraph::m_aVexs[p->num].name << "->";
		p = p->next;
	}
	cout << CGraph::m_aVexs[p->num].name << endl;
}

四、第四阶段:铺设电路规划

实际上就是构建图的最小生成树,使用的是prim法。

构造方法:将顶点分为两个集合S和U,S是已经选择的顶点集合,U是未选择的顶点集合。从U中选择到S中顶点距离最短的顶点i,最短边对应S中顶点j,边i->j就是最小生成树中的边,将i加入集合S,直至U中没有顶点。

在本实验中,因为要保存边,但是不知道具体有多少条边,本来想用链表保存,但是太过麻烦,我想到了容器,于是就用容器来存储边的集合。

代码如下:

void CGraph::FindMinTree()//构建最小生成树
{
	vector<Edge> aPath;
	aPath.clear();
	int *flag = new int[CGraph::m_nVexNum];//flag[i]=0表示顶点i没有被选择
	for (int i = 0; i < CGraph::m_nVexNum; i++)
		flag[i] = 0;
	flag[0] = 1;//从顶点0开始
	int m,n;
	int *prim = new int[CGraph::m_nVexNum];
	prim[0] = 0;
	for (int i = 0; i < CGraph::m_nVexNum-1; i++)//选择总顶点数-1次,一次选择一个顶点
	{
		int min = INT_MAX;
		for (int j = 0; j <= i; j++)//考察未选择的顶点数组中到达顶点prim[j]的距离,选出最短距离
		{
			for (int k = 0; k < CGraph::m_nVexNum; k++)//考察未选择的顶点
			{
				if (flag[k] == 0 && CGraph::m_aAdjMatrix[prim[j]][k] < min&&CGraph::m_aAdjMatrix[prim[j]][k]!=0)
				{
					min = CGraph::m_aAdjMatrix[prim[j]][k];
					m = k;
					n = prim[j];
				}
			}
		}
		flag[m] = 1;
		prim[i + 1] = m;
		Edge p;
		p.vex1 = n;
		p.vex2 = m;
		p.weight = CGraph::m_aAdjMatrix[m][n];
		aPath.push_back(p);
	}
	int size = aPath.size();
	int length = 0;
	for (int i = 0; i < size; i++)
	{
		cout << CGraph::m_aVexs[aPath[i].vex1].name << "->" << CGraph::m_aVexs[aPath[i].vex2].name << "\t" << aPath[i].weight << "m" << endl;
		length += aPath[i].weight;
	}
	cout << "铺设电路的总长度为:" << length << "m" << endl;
}

到这景区景点信息管理系统就完成了,整个实验过程没有大的困难,都是一些小的bug,通过调试都得到了解决。实验用到了数据结构中图的知识,上个学期薛数据结构的时候觉得图很难,不愿意进行图的编程,也觉得哈夫曼编码很难,然后就学的不好,但是这个学期亲自动手去写以后,对哈夫曼树、图的构造、图的深度优先遍历、广度优先遍历以及最小生成树的构建等等都有了很清晰的理解,果然工科不动手是学不好的啊。。。

在旅游景区,经常会遇到游客打听从一个景点到另一个景点的最短路径和最短距离,这类游客不喜欢按照导游图的线路来游览,而是挑选自己感兴趣的景点游览。为于帮助这类游客信息查询,就需要计算出所有景点之间最短路径和最短距离。算法采用迪杰斯特拉算法或弗洛伊德算法均可。建立一个景区旅游信息管理系统,实现的主要功能包括制订旅游景点导游线路策略和制订景区道路铺设策略。 任务中景点分布是一个无向带权连通图,图中边的权值是景点之间的距离。 (1)景区旅游信息管理系统中制订旅游景点导游线路策略,首先通过遍历景点,给出一个入口景点,建立一个导游线路图,导游线路图用有向图表示。遍历采用深度优先策略,这也比较符合游客心理。 (2)为了使导游线路图能够优化,可通过拓朴排序判断图中有无回路,若有回路,则打印输出回路中的景点,供人工优化。 (3)在导游线路图中,还为一些不愿按线路走的游客提供信息服务,比如从一个景点到另一个景点的最短路径和最短距离。在本线路图中将输出任意景点间的最短路径和最短距离。 (4)在景区建设中,道路建设是其中一个重要内容。道路建设首先要保证能连通所有景点,但又要花最小的代价,可以通过求最小生成树来解决这个问题。本任务中假设修建道路的代价只与它的里程相关。 归纳起来,本任务有如下功能模块:(1)创建景区景点分布图;(2)输出景区景点分布图(邻接矩阵)(3)输出导游线路图;(4)判断导游线路图有无回路;(5)求两个景点间的最短路径和最短距离;(6)输出道路修建规划图。(7)主程序用菜单选项供用户选择功能模块。  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值