最短路径算法---dijkstra算法及其优化+A*算法

迪杰斯特拉算法

dijkstra算法的思想类似于广度优先搜索。设集合S为已进入最短路径树的顶点集,集合U为未进入最短路径树的顶点集,图有m个顶点,n条边。核心算法就是寻找在集合U中寻找到出发点花费最少的顶点V1,并将V1加入集合S,然后对顶点V1进行松弛操作。重复该过程直到集合U为空。
为了实现算法,我们需要一些必要的数据结构。一个disTo数组,储存起点到index对应点的目前已知的最小开销,初始时除起始点外数组中其他元素的值都为inf;一个edgeTo数组,储存当前找到的最短路径上连接index点的那一条边,通过这个信息我们才可以回溯路径;一个基于小根堆的优先队列,可以使用C++ STL库中的容器,也可以自己实现一个。

算法的时间复杂度分析,图源自《算法》第四版。
朴素迪杰斯特拉使用基于无序数组的优先队列,时间为\(V+V^2+E\) ~ \(V^2\)
使用基于二叉堆的优先队列后,时间为\(VlogV+VlogV+ElogV\)~\(ElogV\)

A*算法

A*算法使用了启发函数的思想,f(n)=g(n)+h(n)。与dijkstra算法类似的,A*算法每一步的基本操作也是找代价最小的点,然后对该点进行松弛操作。不同的是,A*算法的代价是启发函数的值,实验中我采用欧氏距离作为启发函数的值,可以保证一定得到最优解。在A算法中,最后disTo数组与edgeTo数组中存放的值可能并非最短路径树对应的值,不要与dijkstra算法混淆。g(n)的求解与dijkstra一致,即进行松弛操作。
下面是启发函数的三种情况:
如果h(n)始终小于等于节点n到终点的代价,则A
算法保证一定能够找到最短路径。但是当h(n)的值越小,算法将遍历越多的节点,也就导致算法越慢。
如果h(n)完全等于节点n到终点的代价,则A算法将找到最佳路径,并且速度很快。可惜的是,并非所有场景下都能做到这一点。因为在没有达到终点之前,我们很难确切算出距离终点还有多远。
如果h(n)的值比节点n到终点的代价要大,则A
算法不能保证找到最短路径,不过此时会很快。

继续优化

思路一:以往的策略是算法找到起始点到剩余所有点的最短路径后才结束,现在我们考虑在算法一找到目标点后就结束。
思路二:二叉堆虽然将查询时间降到了对数数量级,但是堆越扁平查询的的速度越快,所以可以考虑继承重写二叉堆优先队列,转换为基于四叉堆、五叉堆的优先队列。

源代码在github仓库中,2023年毕业了再公开。
https://github.com/chuansao825/homework-of-grade3.git

posted @ 2021-11-30 17:51  带带绝缘体  阅读(497)  评论(0)    收藏  举报