航线规划系统设计与实现——数据结构期末大作业(图)

大二上学期一次普普通通的数据结构期末大作业,且DDL在春节期间,所以听起来也不是很普通。下面直接将题目要求和实验报告融合在一起了,省略了操作手册。

一、任务重述

根据给定的 XM 航空公司的现有航线数据,构造该公司的航线图。在此基础上,设计一个简易的航线规划系统。该系统可根据用户提出的不同需求,返回航班线路的解决方案(诸如航班 ID 顺序表)。航线数据见实验作业附件区的 “flflight-data.csv’’ 文件。如图所示,共有 2346 条航线,涉及 79 个机场,142 架飞机,514 个航班。

 二、实验目的

利用图结构、邻接矩阵和邻接表的基本知识,根据给定的 XM 航空公司的现有航线数据,构造该公司的航线图。在此基础上,设计一个简易的航线规划系统。该系统可根据用户提出的不同需求,返回航班线路的解决方案。

三、功能介绍

  1. 完成从任意机场出发的深度优先遍历, 输出所有能够到达的机场编号及路径,所有主要路径中所经机场不能重复。
  2. 完成从任意机场出发的广度优先遍历, 输出所有能够到达的机场编号及路径,所有主要路径中所经机场不能重复。
  3. 仅限直飞或 1 次中转,判断任意两个机场的可连通性,并给出 Flight ID 顺序表。
  4. 求任意两个机场之间的最短飞行时间(含转机停留时间)。输出 Flight ID,总飞行时间,总中转时间,出发时间,达到时间。
  5. 给定起飞时段或者降落时段要求,求经过直飞或者 1 次转机,任意两个机场的多个备选航线。
  6. 给定起飞时段或者降落时段要求,求经过直飞或者 1 次转机,任意两个机场之间的最低航费及对应航线。

 四、整体思路

整体程序由7个主要的头文件和一个主函数组成:flight_reader 对csv文件数据进行读取,将其存储到对应的结构体中并修改时间格式;dfs_and_bfs利用C++栈操作实现深度优先搜索,利用C++队列操作实现广度优先搜索;find_all_flight根据输入的起点和终点,进行所有路径的深度优先搜索,输出直达或一次中转的航班表至result.txt文件,并将路径存储到指定数组;count_fares 和 count_time 利用已经存储的all_route路径数组信息,计算每一路径所需要的时间和花费,从而选出最优路径。

五、详细设计及关键代码

(一)功能点1、2(DFS/BFS)

读取文件后,将每一条航班信息存入结构体内,结构体的内容作为每个信息块的data内容。DFS采用栈操作进行,判断两机场是否相联通时,由于不需选择最短时间或花费的路径,因此判断过程中,在符合B机场出发到C的时间晚于A机场到B机场的时间的所有路径中,选择时间最早的作为出发时间,从而将两个机场之间多组航线数据简化为只有一条最优航线数据,从而实现DFS。例如图中从B到C的两条路径都符合时间要求,则选取出发时间最早的路径作为最优路径。

由于向下深度搜索时需要满足时间先后的要求,程序用二维数组 time_char[ROUTE_NO][CHAR_LEN]存储在目前搜索路径第ROUTE_NO位置的机场,其抵达时间的字符串,从而在栈顶节点搜索为空退栈时,方便获得剩余未退栈节点的时间信息。

深度优先遍历和广度优先遍历的基本思路和所使用的判断是否联通的布偶函数相同,DFS使用栈进行辅助操作,BFS使用队列进行辅助操作。

图 1 两机场是否连通的判断函数

图 2 深度优先搜索函数-栈操作

(二)功能点3(直飞或中转一次的所有路径)

由于下方功能点有对出发、到达时间要求的限制,因此在功能点3的深搜函数中,程序提前加入时间限制的参数,并在主函数调用功能3时,将函数参数赋值为“000000000000”和“999999999999”,保证所有出发时间均在这一个范围内,以消除时间约束。

功能点3之后都需要输出精准的航班ID,可以说功能点1、2都是对机场节点的连通性进行深度搜索,而功能点3之后则是对不同航线(包含不同机场节点、不同飞行时间)的连通性进行深度搜索。由于航线包含了机场节点和两机场节点(faf和i)之间的所有路径,因此在DFS过程中,除了存放机场节点信息的faf_stack外,还引入辅助栈help_stack存放两个机场节点之间的航线编号。每一次向深一层搜索时,实质上是遍历所有路径,若联通,则机场编号入左栈(如下图),航线编号入右栈,以完成一层深度遍历。

 

两个航线相连通的条件是1)两机场相联通 2)时间先后顺序符合 3)该路径未被访问过 4)该节点机场未出现在现在正在搜索的路径中,程序通过借助三维数组faf_visited[DEP_ID][ARR_ID][BRANCH_NO] 判断路径是否被访问过,前两维代表从机场编号DEP到机场编号ARR的路径,BRANCH_NO代表该两机场间的路径编号,从而精确到具体的路径ID。判断机场是否出现在正在搜索的路径中时,程序借助save数组临时存放栈中信息,并且在打印路径时也采用类似方法。

      

图 3 判断机场是否重复出现在路径中的函数                                     图 4 路径示意图

当栈顶继续搜索结果为空,即没有符合要求的路径可以入栈时,将栈顶退栈,包括主栈(存放机场节点编号)和辅助栈(存放路径编号)。由于航线有向图存在多种路径的情况,例如图4中从B到E存在的B->D->E和B->C->D->E两种路径,若先搜了BDE的路径,则DE之间的路径会被标记,如果在D退栈时不删除这个标记,则后续搜索时,由于路径已经被标记过,则不会再搜索出BCDE的路径,从而出现错误。因此,程序在每一个栈顶节点退栈时,删除退栈节点与所联通的所有其他节点之间的标记。

图 5 删除退栈节点与其他节点的路径标记

在搜索时,由于题目限制中转一次或直飞,因此存放路径的栈的大小不得超过3,即起点、中转站、终点,因此在搜索时,通过限制入栈元素个数来控制路径长度,总体搜索函数如下。

 

 

 

图 6 功能点3(题目中的2)深搜代码

(三)功能点4(无时间限制的最短飞行时间)

将功能点3求得的所有路径存入数组,遍历所有路径,借助up和down数组,计算其总体飞行时间、中转时间等。

                                                         图 7 时间计算关键代码

(四)功能点5(有时间限制的所有路径)

由于限制出发时间,程序将输入的限制(不早于和不晚于)作为参数传入深搜函数,在判断是否入栈的语句中,增加条件:对于第一条路经,如果起飞时间在允许的范围内则入栈,不符合不入栈。

 图 8 起飞时间条件限制

(五)功能点6(有时间限制的最小花费)

在功能点5的基础上,将路径信息存入路径数组中,遍历所有路径,求出最小花费。

 图 9 花费计算关键代码

(六)主函数(面向用户进行选择)

六、运行结果展示

功能点1

输入:3

输出:从机场3开始的所有深度搜索路径

 

 

功能点2

输入:3

输出:BFS遍历顺序

功能点3

输入:48 49

输出:48到49的所有路径(直飞或一次转机)写入文件(作为附件上传)

 

功能点4 

输入:49 50

输出:航线ID、出发时间、飞行时间、到达时间、累计时间

  

注:已验算该结果成绩,总共200min,与案例相同;调试过程中还发现如下答案,总共200分钟,换乘5分钟,飞行195分钟。

功能点5

输入:49 50 201705070000 201705091000

输出:该时间范围内起飞的所有49到50的所有路径(直飞或中转一次),并写入文档(文档附在作业文件夹中)。

 

功能点6

输入:50 25 201705051200 201705051400

输出:该时间范围内起飞的所有50到25所有路径中的最小花费

 

最后期末成绩还挺不错的,单看这个大作业是给了90分~

猜你喜欢

转载自blog.csdn.net/weixin_62724756/article/details/129340083