.next即为以x为起点的上一条边。程序中对于边的插入是通过文件读取每条边的起点,止境,长度来插入。
然后是来说明下怎样求最短路:
- //最短路算法
- EdgelenType Dijkstra(Pos p, PosType Start, PosType End) //求两点之间的最短路径,并记下路径,返回路径
- {
- memset(vis, 0, sizeof(vis)); //初始化vis数组,指所有元素均未访问国
- memset(dis, Maxx, sizeof(dis)); //初始化dis数组,标记所有节点到start数组距离均为无穷大
- path = (int *)malloc(sizeof(int) * TotalEdge); //存经过路径
- Mh m = InitMinheap(TotalEdge); // 边的个数
- dis[Start] = 0; //自己到自己的距离为0
- tmp.dis = 0, tmp.x = Start; //赋tmp值
- pushMinHeap(m, tmp); //将第一个点放入堆中
- path[Start] = 0; //记录下起点的path值为0,方便后序访问
- //遍历len,找更新所有和end的距离dis
- while (!IsEmptyHeap(m)) //如果堆不为空
- {
- tmp = getHeapTop(m); //取出堆顶元素(即dis最小的下标)
- popMinHeap(m); //取出下标
- if (vis[tmp.x]) //如果该下标访问过
- continue; //跳过
- vis[tmp.x] = 1; //未访问过就将其标记
- for (i = head[tmp.x]; i != -1; i = edge[i].next) //遍历该点所连的边
- {
- y = edge[i].t; // y值为该边所指向的终点
- if (edge[i].len + dis[tmp.x] < dis[y]) //如果start到x的长度(dis[x]) + 该边长度 小于 start到X的长度
- {
- dis[y] = edge[i].len + dis[tmp.x]; //更新start到y的长度(dis[y])
- path[y] = tmp.x; //记录到y,所经过点x
- tmp2.x = y, tmp2.dis = edge[i].len + dis[tmp.x];
- pushMinHeap(m, tmp2); //压入堆
- }
- }
- }
- FreeMinHeap(m); //释放堆内存
- return dis[End]; //返回start到end的最短距离
- }
复制代码 利用堆优化+迪杰斯特拉算法,vis数组表现起点到该点的最短间隔是否已经求出。若为1,表现已求出,为0表现未求出。先初始化vis数组全为0,表现此时起点到该点的最短间隔均未求出。dis数组表现起点到其他点的最短间隔,先初始化为最大值。
开始盘算最短路,每次从小顶堆顶中取出与起点间隔最短的点,然后判定该点是否访问过,便是否已经求出起点到该点的最短间隔,访问过就跳过该点,未访问过,就将该点x作为中心节点来更新起点到其他未求出最短间隔的节点的间隔,并标记该点访问过了。如果起点到该点的间隔dis[x]+该点到其他点y的间隔<起点到y的最短间隔dis[y],就更新这个最短间隔dis[y],然后将该点y的信息放入堆中。
输出道路:
- //输出道路算法
- void PrintPath(Pos p, PosType start, PosType end, char *E) //输出道路
- {
- if (path[end] == 0) //因为起始点的path为0,因此此时递归到起始点
- {
- printf("%s", p[start].name); //输出起始点
- return;
- }
- else
- {
- PrintPath(p, start, path[end], E); //一直递归到起始点后,开始输出
- if (!strcmp(p[end].name, E)) //如果当前点的下一个为终点
- strcpy(door, p[path[end]].name); //保存当前门(终点为教室,门为教室的上一个点)
- printf("->%s", p[end].name); //输出当前遍历的点名
- }
- }
复制代码 在迪杰斯特拉算法中,还维护了一个数组path。先将path[start] = 0,然后每次更新到起点到其他节点y的最短间隔时,也同时更新y的path,即path[y] = x(中心节点),即path数组,除了起点外,其存储的为最短间隔要经过path下标节点的道路的上一个节点。
然后在输出道路的函数中,递归输出,即从止境的path值开始递归,直到止境的path值为0,即此时止境的path值和起点相同,输出起点,这时回退,输出上一层止境的path值,再输出点,以此类推。
最小堆:
- Status pushMinHeap(Mh m, Index x) //将x存入堆
- {
- m->last++; //堆数组最后的下标+1
- m->i[m->last] = x; //将X存入堆数组的最后一个位置。
- parent = m->last / 2, i = m->last; // parent指向最后一个元素的父亲节点下标,i指向最后一个元素下标
- while (m->i[parent].dis > x.dis && parent != 0) //从下往上滤
- {
- swap(m->i[parent], m->i[i]); //因为为最小堆,如果父亲节点大于儿子节点,就交换
- i = parent; //儿子 = 父亲
- parent /= 2; //父亲 = 父亲的父亲
- }
- return OK;
- }
复制代码 对于堆的插入操作,起首是将堆的最后一个下标自增1,即last++,然后将插入的元素x插入堆中数组下标为last值的位置,开始从下到上调整堆的布局。即用一个指针i指向x元素的下标,一个指针p指向x元素的父亲节点,开始比较两者巨细,如果父亲节点大于孩子节点而且此时父亲节点不为0(由于数组下标从1开始),说明不满足最小堆的要求,将二者调换,然后i指向p,p再指向i的父亲,再次比较,以此类推,直到父亲节点小于孩子节点,大概父亲节点下标为0为止。
- Status popMinHeap(Mh m) //将堆头元素删除
- {
- m->i[1] = m->i[m->last]; //先将第一个赋值为最后一个元素,也算删除了第一个元素
- i = 1; // i指向第一个元素的下标
- m->last--; //最后一个元素下标-1
- child = i * 2; //指向第一个元素的左孩子
- if (m->i[child].dis > m->i[child + 1].dis && child + 1 <= m->last) //如果有右孩子,且右孩子比左孩子小
- child++; //那么就指向右孩子
- while (m->i[child].dis < m->i[i].dis && child <= m->last) //从上往下滤
- {
- swap(m->i[child], m->i[i]); //如果i节点指向元素比最小的孩子还小,就交换
- i = child; // i指向孩子
- child *= 2; //孩子指向孩子的孩子
- if (m->i[child].dis > m->i[child + 1].dis && child + 1 <= m->last) //再次找最小的孩子
- child++;
- }
- return OK
- }
复制代码 对于堆的删除操作,由于堆中只能删除堆顶元素,即取出最小元素,那么就将堆中数组的最后一个元素提上来覆盖掉堆顶元素,然后开始从上往下调整。指针i指向根结点,指针c指向i的左孩子,先比较i的左孩子和右孩子谁更小,如果右孩子更小,则c指向右孩子。然后开始比较i指向的值和孩子节点值的巨细,如果孩子节点的值小于父亲节点,说明不符合最小堆的性质,这时将两个节点进行交换,然后再将i指向c节点, c指向i的最小的孩子节点,然后再次比较,以此类推,直到符合最小堆性质为止。
四、实现细节说明
求最短路径:迪杰斯特拉+堆优化,
利用迪杰斯特拉算法,用数组的下标表现点的编号,利用链式前向星头脑来找每一个点的边,利用最小堆来存储每个点到起点的间隔,然后每次从堆顶取出到起点间隔最小的点来更新其他点到起点的最小间隔,每次更新将该点的上一个点给记住,方便后序输出道路。
如以上界面,输入起始地点,止境。然后输出最短间隔和道路。
体系根据用户输入的起点,先从体系中找该点是否存在,存在则继承下一步,,不存在就让用户再次输入。然后是判定用户输入的止境是否存在,且是否为课堂,若不满足要求,则再次输入。若为课堂则继承下一步,利用迪杰斯特拉算法盘算两点间的最短间隔,并生存路线,然后输出。
五、测试数据及测试效果
大体说明:
根据我们的测试效果,与我们校园实地的环境是同等的。
校园舆图测试:
导航路线测试:
具体说明:
起首说明下体系中文件的导入:
点文件:用来存放所有舆图上要初始化的点,即用户可以输入的地点,体系根据该文件来建点。
第一个311指总共有311个点。
然后每行一个地点。
边文件:用来存放所有边的文件,体系通过该文件内容建边。
第一个1121指总共有1121条边。
每行一条边,按照起点、止境、长度分列顺序。比方第二行的指从秋实西1到厚德北1长度为161米,且厚德北1到秋实西1也有一条161的边,实际上一举动两条边。
然后是先容用户输入所实现的功能:
输入1,输出简化舆图:
输入2,根据用户输入的起点和止境课堂,输出路线。
如图所示,选择起点为本源南1,止境课堂为南4-302。然后程序给出了具体走法,该走法可以参照舆图来理解。
再次输入:
这次输入的起点为厚德北1,止境课堂为为北3-204。
如图所示为程序给出的最佳路径。
输入3,退出体系,并释放内存。
六、总结
(一)体系特点及创新
特点:
1、简洁输出关键有用的信息,输出界面有简化舆图的展示,输出间隔和路线简洁明白。
2、更人性化,到达讲授楼地域,有笔墨引导方向至具体课堂。
创新性:
1、利用地点名到序号的映射,使代码写起来更清晰易懂。可以直接通过地点名来进行建图。
2、利用链式前向星的头脑来建边,可以使访问一个点所连的边更快,避免了访问其他没有连边的节点。
3、利用堆来优化迪杰斯特拉算法,使算法效率提升。
4、利用数组递归方式来输出最短路线。
5、联合实际,将每个门怎样到达课堂的具体路线完善。
(二)遇到的标题及办理思路
1、路线间隔丈量困难
办理方法:我们通过查阅资料,在电脑端高德舆图找到了测距尺,快速确定了各个地点间的间隔。
2、地点名难与序号逐一对应
办理方法:刚开始考虑的是利用哈希表,然后思索怎样界说一种求有用快速的关键字的筹划,实现更好的根据名字来定位元素。但是由于地点名为中文,有关中文怎样编码不是很清晰,有想过将中文改为拼音来建图。但是拼音很容易就会打堕落,对于体系维护来说不是很方便,因此就利用了c++的stl内里的map布局,来实现名字对应序号,至于序号对应名字,就可以利用布局体数组的下标来实现。
3、建点和建边部分的程序很长,且没太大意义
办理方法:将其用文件的方式输出。刚开始利用文件,发现根本运行不了,发现原来是对于中文的编码,文件要选择ANSI的格式。
4、程序输出最佳路线无法明白指向课堂
办理方法:将讲授楼的大门与每一个可以从该门口到达的课堂连一条为0长度的边,这样用户输入课堂止境时,其路线的止境就会指向课堂了。
5、对于用户输入的起点和止境缺少判定,导致随便输入代码也会继承执行堕落
办理方法:起首是对起始点的判定,起始点输入的,要为点文件中给出的已经初始化的点,通过map的性质进行判定,如果用户输入的点不在map中,那么直接该点的map值为0,而我们初始化点的序号从1开始,就可以判定了。对于止境输入是否为课堂的判定,读取点文件,看输入的课堂是否存在来判定。
最后,可能以上的程序照旧存在瑕疵,各人可以得当修改和完善,感谢阅读!
完整代码:
【免费】数据据布局课程筹划-zzu校园导航(迪杰斯特拉算法)资源-CSDN文库
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) |
Powered by Discuz! X3.4 |