1、数 据 结 构2实 验 报 告1、实验目的1) 加深对图的表示法和图的基本操作的理解,并可初步使用及操作;2) 掌握用图对实际问题进行抽象的方法,可以解决基本的问题;3) 掌握利用邻接表求解非负权值、单源最短路径的方法,即利用迪杰斯特拉算法求最短路径,同时掌握邻接表的建立以及使用方法,能够解决相关的问题;4) 学会使用 STL 中的 map 抽象实际问题,掌握 map,List, ,priority_queue 等的应用。二、实验内容与实验步骤( 1) 简短明确地写出实验的内容使用图这种抽象的数据结构存储模拟的欧洲铁路路线图,通过迪杰斯特拉算法求出欧洲旅行最少花费的路线。该实验应用迪杰斯特拉算
2、法求得任意两个城市之间的最少路费,并给出路费最少的路径的长度和所经过的城市名。( 2) 简短描述抽象数据类型或设计的函数描述,说明为什么要使用这种抽象数据类型,并说明你的解决设想抽象数据类型class City:维护一个城市的信息,包括城市名 name,是否被访问过的标记 visited,从某个城市到达该城市所需的总费用 total_fee 和总路径长度 total_distance,求得最短路径后路径中到达该城市的城市名 from_city。class RailSystem:用邻接表模拟欧洲铁路系统,该邻接表使用数据结构 map 实现,map 的 cities-outgoing_servic
3、es 值对的数据类型分别为 string 和 list,对应出发城市名和该城市与它能够到达的城市之间的 Service 链表。class Service:为铁路系统模拟了两个城市之间的直接路线,包括两个城市之间直接到达的费用 fee,两城市之间的直接距离 distance,还有经过的城市 destination。课程名称:数据结构 班级:软件赴日 1101 实验成绩:实验名称:欧洲旅行 学号:20112271 批阅教师签字:实验编号:实验二 姓名:贾志远 实验日期:20013 年 6 月 20 日指导教师: 组号: 实验时间: 20 时 03 分 22 时 43 分3部分设计函数描述 Rail
4、System(const string输出两城市间的最少费用的路径,调用calc_route(string from, string to)函数计算最少费用 calc_route(string from, string to)使用迪杰斯特拉算法计算from和to两个城市间的最少费用的路径(3) 简短明确地写出你实验所采用的存储结构及其用途,详细说明其中的属性的含义。1) map outgoing_services用来保存由一个城市出发可以直接到达的城市名及这两个城市之间的路径信息。 2) list ms以 service 为指针的 list 表,保存两城市间的路径。3) map cities用
5、来保存所有城市信息,通过城市名查找该城市有关信息。4) priority_queue, Cheapest candidates存储候选的遍历城市,City*是优先队列存储的对象类型,vector 是该对象的向量集合,Cheapest 是比较规则。三、实验环境操作系统 Win7、调试软件 VS2012四、实验过程与分析( 1) 描述你在进行实现时,主要的函数或操作内部的主要算法,分析这个算法的时、空复杂度,并说明你设计的巧妙之处。该实验主要用到了迪杰斯特拉算法,这个算法要求所有边的权值非负,提出了按路径长度递增的顺序逐步产生最短路径的算法,首先求出长度最短的一条路径,然后参照它求出长度次短的一条
6、路径,以此类推,指导顶点到其他顶点的最短路径全部求完为止即可解决该实验的问题。算法的时间复杂度是 ,空间复杂度为)(2nO)(nO1) calc_route(string from, string to)函数利用优先权队列和迪杰斯特拉算法,计算任意两城市之间费用最少的路径,优先权队列按照费用由大到小的顺序入队。首先初始化所有城市的信息。通过迭代器遍历它的邻接链表,得到邻接城市名,当这个城市未被被访问过且从弹出的城市到该城市的费用大于这两个邻接城市间费用和出发城市目前的最少费用之和,更新从出发城市到该城市的费用,并且记录这个城市的经由城市为弹出的城市名并将这个城市入栈,同时更新目前的路径长度。代
7、码如下:4priority_queue, Cheapest candidates;City* city=citiesfrom;city-total_fee=0;city-total_distance=0;candidates.push(city);while(!candidates.empty()while(city = candidates.top()-visited)candidates.pop();city-visited=true;candidates.pop();list service_list = outgoing_servicescity-name;for(Service* s
8、ervice;!service_list.empty();service_list.pop_front()service = service_list.front();if(citiesservice-destination-total_fee(city-total_fee+service-fee)citiesservice-destination-total_fee = city-total_fee + service-fee;citiesservice-destination-from_city = city-name;citiesservice-destination-total_dis
9、tance = city-total_distance + service-distance;candidates.push(citiesservice-destination);if (citiesto-visited)return pair(citiesto-total_fee,citiesto-total_distance);elsereturn pair(INT_MAX, INT_MAX);在该算法中使用了优先队列对其进行了优化,使得整个算法在实际的运行时间上有了明显的缩小。2) load_services(string)函数读入 from 和 to 城市后,首先判断这两个城市在 ci
10、ties 这个 map 中是否存在,若不存在则添加到 map 中。判断过程需要遍历整个 map,这里利用了 map 的 find()函数,减少了空间和时间的使用。代码如下 :ifstream inf(filename.c_str();string from, to;int fee, distance;while (inf.good()inf from to fee distance;if (inf.good()map :iterator mp = outgoing_services.find(from);if(mp=outgoing_services.end()list ms;ms.push_
11、front(new Service(to,fee,distance);outgoing_services.insert(pair(from, ms);5cities.insert(pair(from, new City(from);else(mp-second).push_front(new Service(to,fee,distance);inf.close();3) recover_route(const stringstring all=city;while(back = cities.find(back)-second-from_city)!=“)all = back+“ to “+a
12、ll;return all;( 2) 你在调试过程中发现了怎样的问题?又做了怎样的改进?priority_queue 堆在内部对象数据更改后无法进行重排。发现在 vs2012 会返回错误,但是点击忽略后还是可以直接运行得到结果。如图:点击忽略后出现结果,如图:6五、实验结果总结回答以下问题:(1) 你的测试充分吗?为什么?你是怎样考虑的?答:我的测试充分,对每条线都进行了双向检验并且测试了不存在的城市名,返回了如图结果:而对存在的城市则返回如图:对同一个城市则返回(2) 在你的问题解决方案中,为树或图选取了顺序的还是链式的存储结构?为什么要选取顺序的或链式的存储结构?在我的问题解决方案中选取了
13、链式的存储结构。由于该图是一个稀疏图,采用顺序存储结构对空间的浪费较大,并且对于采用迪杰斯特拉算法,无论采用那种存储结构算法的时间复杂度都是相同的。所以我选择了链式的存储结构。7(3) 用一段简短的代码及说明论述你的应用中主要的函数的主要处理部分。priority_queue, Cheapest candidates;City* city=citiesfrom;city-total_fee=0;city-total_distance=0;candidates.push(city);while(!candidates.empty()while(city = candidates.top()-vi
14、sited)candidates.pop();city-visited=true;candidates.pop();list service_list = outgoing_servicescity-name;for(Service* service;!service_list.empty();service_list.pop_front()service = service_list.front();if(citiesservice-destination-total_fee(city-total_fee+service-fee)citiesservice-destination-total
15、_fee = city-total_fee + service-fee;citiesservice-destination-from_city = city-name;citiesservice-destination-total_distance = city-total_distance + service-distance;candidates.push(citiesservice-destination);if (citiesto-visited)return pair(citiesto-total_fee,citiesto-total_distance);elsereturn pai
16、r(INT_MAX, INT_MAX);from 城市先入队,弹出队列中第一个城市名,即当前费用最少的路径的目标城市,在outgoing_services 中找到它对应的城市并取得它对应的 list 的迭代器,通过迭代器遍历它的邻接链表,得到邻接城市名,判断城市是否被访问了,如果这个城市未被访问且从弹出的城市到该城市的费用大于这两个邻接城市间费用和出发城市目前的最少费用之和,更新从出发城市到该城市的费用,记录这个城市名并将这个城市入栈,同时更新当下的路径长度。(4) 树和图的应用算法中,无论是递归或非递归的算法都要用到栈这种数据结构,试论述你的算法设计中,栈是被显式地还是隐式地使用?栈内元素可
17、能达到的最大数是多少?在图中,使用了栈的数据结构来优化算法的性能。程序在最后恢复所走路径的函数中隐式地使用了栈。用一个 while 循环,将栈中的元素依次弹出就是从 from 城市到 to 城市费用少的路线。(5) 源程序的大致的执行过程是怎样的?8首先读文件将所有数据赋值,通过键盘输入旅行的起点和终点,在所给文件信息范围内则声明一个集合,用于存储最短路径的集合,然后根据周围的城市选择花费最小的路径,添加到集合中,最后返回路径信息。其中,读取文件创建图的存储即构造 RailSystem 的实例,输出路径的函数中调用计算费用最少的路径的方法 calc_route(string from, str
18、ing to) 和恢复求得的费用最少的路径的函数 recover_route(const string& city)。请清晰、准确、详细地回答上面的问题,要求标点符号正确无误,图表表示符合规范。你的报告应至少超出一页的文字描述,注意你描述的文字一定要叙述流畅,具有较好的逻辑性。六、附录(1) 实验的参考资料任燕编著 数据结构 C+描述.清华大学出版社(2) 思考题a) 在你的应用中使用了中的哪些容器?有什么用途?答:使用了 STL 中的列表(list)由节点组成的双向链表,每个结点包含着一个元素 ;栈(stack)后进先出的值的排列 ;优先队列(priority_queue) 元素的次序是由作
19、用于所存储的值对上的某种谓词决定的的一种队列 ;映射(map) 由键,值对组成的集合,目的地是否已被访问判断城市是否已被访问过且费用是否比原费用少把元素加入堆从堆栈中取出最优化路径和城市(元素)是是否否找出服务队列,找出最小花费路径开始程序去除重复元素结束程序9以某种作用于键对上的谓词排列 。例举部分用途:1) map outgoing_services用来保存由一个城市出发可以直接到达的城市名及这两个城市之间的路径信息 2) list以 service 为指针的 list 表,保存两城市间的路径3) map cities用来保存所有城市信息,通过城市名查找该城市有关信息4) priority_queue, Cheapest 存储候选的遍历城市,City*是优先队列存储的对象类型,vector是该对象的向量集合,Cheapest 是比较规则b) 假设一个图采用邻接多重表存储结构进行存储,在某一个结点的邻接结点链表中,结点的顺序是否会影响遍历的次序?答:会。因为邻 接 表 是 图 的 一 种 链 式 存 储 结 构 , 对 图 的 每 个 顶 点 建 立 一 个 单 链 表 , 第 i 个 单 链表 中 的 结 点 包 含 顶 点 Vi 的 所 有 邻 接 顶 点 , 它的遍历与链表的遍历相同,当一个图采用邻接多重表存储结构进行存储时会影响遍历的次序。