当前位置:文档之家› 数据结构实验二-

数据结构实验二-

实 验 报 告一、实验目的1) 加深对图的表示法和图的基本操作的理解,并可初步使用及操作; 2) 掌握用图对实际问题进行抽象的方法,可以解决基本的问题;3) 掌握利用邻接表求解非负权值、单源最短路径的方法,即利用Dijkstra 算法求最短路径,同时掌握邻接表的建立以及使用方法,能够解决相关的问题;4) 学会使用STL 中的map 抽象实际问题,掌握map ,List,,priority_queue 等的应用。

二、实验内容与实验步骤(1) 实验内容:使用图这种抽象的数据结构存储模拟的欧洲铁路路线图,通过Dijkstra 算法求出欧洲旅行最少花费的路线。

该实验应用Dijkstra 算法求得任意两个城市之间的最少路费,并给出路费最少的路径的长度和所经过的城市名。

(2) 抽象数据类型及设计函数描述1) 抽象数据类型class City :维护一个城市的信息,包括城市名name ,是否被访问过的标记visted ,从某个城市到达该城市所需的总费用total_fee 和总路径长度total_distance ,求得最短路径后路径中到达该城市的城市名from_city 。

class RailSystem :用邻接表模拟欧洲铁路系统,该邻接表使用数据结构map 实现,map 的key-value课程名称:数据结构 班级: 实验成绩: 实验名称:欧洲旅行 学号: 批阅教师签字:实验编号:实验二 姓名: 实验日期:2013 年6 月 18 日 指导教师: 组号:实验时间:值对的数据类型分别为string和list<*Service>,对应出发城市名和该城市与它能够到达的城市之间的Service链表。

class Service:为铁路系统模拟了两个城市之间的直接路线,包括两个城市之间直接到达的费用fee,两城市之间的直接距离distance。

部分设计函数描述●RailSystem(const string& filename)构造函数,调用load_services(string const &filename)函数读取数据●load_services(string const &filename)读取传入的文件中的数据并建立上述两个map以模拟欧洲铁路路线图●reset(void)遍历cities图,初始化所有城市的信息:visted未访问,total_distance最大值,total_fee费用最大值,from_city为空●~RailSystem(void)析构函数,用delete将两个map中所有使用new操作符开辟的空间删除●void output_cheapest_route(const string& from, const string&to, ostream& out);输出两城市间的最少费用的路径,调用calc_route(string from, string to)函数计算最少费用●calc_route(string from, string to)使用Dijkstra算法计算from和to两个城市间的最少费用的路径(3)采用的存储结构1)map<string, list<Service*> > outgoing_services用来保存由一个城市出发可以直接到达的城市名及这两个城市之间的路径信息。

2)list<Service*>以service为指针的list表,保存两城市间的路径。

3)map<string, City*> cities用来保存所有城市信息,通过城市名查找该城市有关信息。

4)priority_queue<City*, vector<City*>, Cheapest> candidates存储候选的遍历城市,City*是优先队列存储的对象类型,vector<City*>是该对象的向量集合,Cheapest是比较规则。

三、实验环境操作系统:Windows 8调试软件:Microsoft visual studio 2012上机地点:综合楼311机器台号:笔记本四、实验过程与分析(1)主要算法该实验主要用到了Dijkstra算法,这个算法要求所有边的权值非负,提出了按路径长度递增的顺序逐步产生最短路径的算法,首先求出长度最短的一条路径,然后参照它求出长度次短的一条路径,以此类推,指导顶点到其他顶点的最短路径全部求完为止即可解决该实验的问题。

1)calc_route(string from, string to)函数利用优先权队列和Dijkstra算法,计算任意两城市之间费用最少的路径,优先权队列按照费用由大到小的顺序入队。

首先初始化所有城市的信息。

通过迭代器遍历它的邻接链表,得到邻接城市名,当这个城市未被被访问过且从弹出的城市到该城市的费用大于这两个邻接城市间费用和出发城市目前的最少费用之和,更新从出发城市到该城市的费用,并且记录这个城市的经由城市为弹出的城市名并将这个城市入栈,同时更新目前的路径长度。

代码如下:reset();//初始化所有城市信息cities[from]->total_fee = 0;cities[from]->total_distance = 0;candidates.push(cities[from]); //将出发城市入队while(!candidates.empty()){string from_city = candidates.top()->name; //获得出发城市名以便在后面得到它直接到达的城市名candidates.top()->visited = true;candidates.pop();list<Service*>::iterator siter = outgoing_services[from_city].begin();//在outgoing_services中找到from对应的城市并取得from对应的list的迭代器while(siter != outgoing_services[from_city].end()){string dest = (*siter)->destination; //得到链表中的出发城市可以直接到达的城市if((cities[dest]->visited == false)&&((*siter)->fee +cities[from_city]->total_fee < cities[dest]->total_fee)){cities[dest]->total_fee = (*siter)->fee +cities[from_city]->total_fee;cities[dest]->total_distance = (*siter)->distance +cities[from_city]->total_distance;cities[dest]->from_city = from_city;candidates.push(cities[dest]); //将与from邻接的destination入队当循环结束时就是当前链表中费用最小的城市}siter ++;}}该算法的时间复杂度为O(N2),在该算法中使用了优先队列对其进行了优化,优先队列的插入与删除的时间都是错误!未找到引用源。

,在使得整个算法在实际的运行时间上有了明显的缩小。

2)load_services(string)函数读入from和to城市后,首先判断这两个城市在cities这个map中是否存在,若不存在则添加到map中。

判断过程需要遍历整个map,这里利用了map的find()函数,减少了空间和时间的使用。

代码如下:if(cities.find (from) == cities.end()){//from不在cities中将其插入City *cfrom = new City(from);cities.insert(pair<string,City*>(from,cfrom));}此外,需要判断outgoing_services中是否有from这个城市,若没有将它添加到outgoing_services中。

添加时先创建一个service保存from城市到to城市的信息。

代码如下:Service *service = new Service(to,fee,distance);if(outgoing_services.find(from) == outgoing_services.end()){//from不在outgoing_services中则to也不在list中将它们插入outgoing_services list<Service*> *l = new list<Service*>();outgoing_services.insert(pair<string,list<Service*>>(from,*l));}outgoing_services[from].push_back(service);3)recover_route(const string& city)函数利用栈的先进先出特性,将求得的路径所经由的城市名入栈再弹栈,则得到正序的路径。

主要代码如下:stack<string> path; //定义栈string final_path;string c = city;path.push(city); //将目标城市入栈while(cities[c]->from_city != ""){ //目标城市经过的城市不为空时path.push(cities[c]->from_city); //将目标城市经过的城市入栈c = cities[c]->from_city; //得到下一个经过的城市}while(path.size()!=1){ //避免多输入一个to 判断到栈的大小为时停止final_path+= (path.top() + " to "); //输出各个经过的城市名path.pop(); //将栈内的城市名弹栈更新top指向的值}final_path += path.top(); //连接最后一个城市即出发城市path.pop(); //将出发城市弹栈return final_path;(2)调试过程中发现的问题及改进方法问题:当向outgoing_services中插入list<Service*>时,插入不止一次,当将处理outgoing_services的代码中插入list的语句移到循环外时,先插入一个空的list,再将数据push进这个list中即可解决该问题。

相关主题