算法解析

  • 这是一个非常典型的搜索问题。
  • 人物的起点就是他当下所在的位置,终点就是鼠标点击的位置。
  • 我们需要在地图中,找一条从起点到终点的路径。
  • 这条路径要绕过地图中所有障碍物,并且看起来要是一种非常聪明的走法。所谓“聪明”,笼统地解释就是,走的路不能太绕。理论上讲,最短路径显然是最聪明的走法,是这个问题的最优解。

实际上,像出行路线规划、游戏寻路,这些真实软件开发中的问题,一般情况下都不需要非得求最优解(也就是最短路径)
在权衡路线规划质量和执行效率的情况下,我们只需要寻求一个次优解就足够了。
如何快速找出一条接近于最短路线的次优路线呢?
A 算法*:A* 算法是对 Dijkstra 算法的优化和改造。最优出行路线规划问题中,如果图非常大,Dijkstra 最短路径算法的执行耗时会很多
Dijkstra 算法有点儿类似 BFS 算法,它每次找到跟起点最近的顶点,往外扩展。这种往外扩展的思路,其实有些盲目。

可以避免“跑偏”吗?

当遍历到某个顶点时,从起点到这个顶点的路径长度是确定的,记作 g(i)(i 表示顶点编号)

  • 虽然从这个顶点到终点的路径长度是未知的,但可以用其他估计值来代替。
  • 可以通过这个顶点跟终点之间的直线距离(欧几里得距离),近似估算这个顶点跟终点的路径长度(注意:路径长度跟直线距离是两个概念)
  • 把这个距离记作 h(i)(i 表示这个顶点的编号),专业的叫法是启发函数(heuristic function)。
  • 因为欧几里得距离的计算公式,会涉及比较耗时的开根号计算,所以一般通过另外一个更加简单的距离计算公式,那就是曼哈顿距离(Manhattan distance)。
  • 曼哈顿距离是两点之间横纵坐标的距离之和。计算的过程只涉及加减法、符号位反转,所以比欧几里得距离更加高效。

原来只是单纯地通过顶点与起点之间的路径长度 g(i),来判断谁先出队列,现在有了顶点到终点的路径长度估计值,通过两者之和 f(i)=g(i)+h(i),来判断哪个顶点该最先出队列。
综合两部分,就能有效避免“跑偏”。f(i) 的专业叫法是估价函数(evaluation function)

** A 算法就是对 Dijkstra 算法的简单改造*
在 A* 算法的代码实现中,顶点 Vertex 类的定义,跟 Dijkstra 算法中的定义,稍微有点儿区别,多了 x,y 坐标,以及刚刚提到的 f(i) 值。图 Graph 类的定义跟 Dijkstra 算法中的定义一样。


private class Vertex {public int id; // 顶点编号IDpublic int dist; // 从起始顶点,到这个顶点的距离,也就是g(i)public int f; // 新增:f(i)=g(i)+h(i)public int x, y; // 新增:顶点在地图中的坐标(x, y)public Vertex(int id, int x, int y) {this.id = id;this.x = x;this.y = y;this.f = Integer.MAX_VALUE;//!!!this.dist = Integer.MAX_VALUE;}
}
// Graph类的成员变量,在构造函数中初始化
Vertex[] vertexes = new Vertex[this.v];
// 新增一个方法,添加顶点的坐标
public void addVetex(int id, int x, int y) {vertexes[id] = new Vertex(id, x, y)
}

A* 算法的代码主要有 3 点区别:

  • 优先级队列构建的方式不同,
    A* 算法是根据 f 值( f(i)=g(i)+h(i))来构建优先级队列,
    Dijkstra 算法是根据 dist 值(g(i))来构建优先级队列;

  • A* 算法在更新顶点 dist 值的时候,会同步更新 f 值;

  • 循环结束的条件也不一样。Dijkstra 算法是在终点出队列的时候才结束,A* 算法是一旦遍历到终点就结束。


public void astar(int s, int t) { // 从顶点s到顶点t的路径int[] predecessor = new int[this.v]; // 用来还原路径// 按照vertex的f值构建的小顶堆,而不是按照distPriorityQueue queue = new PriorityQueue(this.v);boolean[] inqueue = new boolean[this.v]; // 标记是否进入过队列vertexes[s].dist = 0;vertexes[s].f = 0;queue.add(vertexes[s]);inqueue[s] = true;while (!queue.isEmpty()) {Vertex minVertex = queue.poll(); // 取堆顶元素并删除for (int i = 0; i < adj[minVertex.id].size(); ++i) {Edge e = adj[minVertex.id].get(i); // 取出一条minVetex相连的边Vertex nextVertex = vertexes[e.tid]; // minVertex-->nextVertexif (minVertex.dist + e.w < nextVertex.dist) { // 更新next的dist,fnextVertex.dist = minVertex.dist + e.w;nextVertex.f = nextVertex.dist+hManhattan(nextVertex, vertexes[t]);predecessor[nextVertex.id] = minVertex.id;if (inqueue[nextVertex.id] == true) {queue.update(nextVertex);} else {queue.add(nextVertex);inqueue[nextVertex.id] = true;}}if (nextVertex.id == t) { // 只要到达t就可以结束while了queue.clear(); // 清空queue,才能推出while循环break; }}}// 输出路径System.out.print(s);print(s, t, predecessor); // print函数请参看Dijkstra算法的实现
}

A 这是为什么不能找到最短路线呢?*
要找出起点 s 到终点 t 的最短路径,最简单的方法是,通过回溯穷举所有从 s 到达 t 的不同路径,然后对比找出最短的那个。但回溯算法的执行效率非常低,是指数级的

Dijkstra 算法在此基础之上,利用动态规划的思想,对回溯搜索进行了剪枝,只保留起点到某个顶点的最短路径,继续往外扩展搜索。动态规划相较于回溯搜索,只是换了一个实现思路,但它实际上也考察到了所有从起点到终点的路线,所以才能得到最优解。

  • A* 算法之所以不能像 Dijkstra 算法那样,找到最短路径,主要原因是两者的 while 循环结束条件不一样
  • Dijkstra 算法是在终点出队列的时候才结束,A* 算法是一旦遍历到终点就结束
  • 对于 Dijkstra 算法,当终点出队列时,终点的 dist 值是优先级队列中所有顶点的最小值,即便再运行下去,终点的 dist 值也不会再被更新了。
  • 对于 A* 算法,一旦遍历到终点,我们就结束 while 循环,这个时候,终点的 dist 值未必是最小值。
  • A* 算法利用贪心算法的思路,每次都找 f 值最小的顶点出队列,一旦搜索到终点就不在继续考察其他顶点和路线了。

所以,它并没有考察所有的路线,也就不可能找出最短路径了。

笔记整理来源: 王争 数据结构与算法之美

【数据结构与算法】【算法思想】 A *搜索算法相关推荐

  1. 数据结构与算法之-----图(搜索算法)

    [ 写在前面的话:本专栏的主要内容:数据结构与算法. 1.对于​​​​​​​初识数据结构的小伙伴们,鉴于后面的数据结构的构建会使用到专栏前面的内容,包括具体数据结构的应用,所使用到的数据结构,也是自己 ...

  2. (王道408考研数据结构)第六章图-第四节5:最短路径之弗洛伊德算法(思想、代码、演示、答题规范)

    文章目录 一:动态规划基本思想 二:弗洛伊德(Floyd)算法基本思想 三:弗洛伊德(Floyd)算法代码实现 四:弗洛伊德(Floyd)算法代码视频演示 五:弗洛伊德(Floyd)算法代码答题规范 ...

  3. (王道408考研数据结构)第六章图-第四节4:最短路径之迪杰斯特拉算法(思想、代码、演示、答题规范)

    文章目录 一:BFS算法局限性 二:迪杰斯特拉(dijkstra)算法基本思想 三:迪杰斯特拉(dijkstra)算法代码实现 四:迪杰斯特拉(dijkstra)算法代码视频演示 五:迪杰斯特拉(di ...

  4. (王道408考研数据结构)第六章图-第四节3:最短路径之BFS算法(思想、代码、演示、答题规范)

    文章目录 一:BFS算法基本思想 二:BFS算法代码 三:反思 最短路径shortestpath):主要有以下两类最短路径问题 单源最短路径问题:一个顶点到其他顶点最短路径 迪杰斯特拉算法(dijks ...

  5. (王道408考研数据结构)第六章图-第四节1:最小生成树之普利姆算法(思想、代码、演示、答题规范)

    文章目录 一:普利姆(Prim)算法算法思想 二:普利姆(Prim)算法注意点 三:普利姆(Prim)算法代码实现 四:普利姆(Prim)算法代码视频演示 五:普利姆(Prim)算法动画演示 六:普利 ...

  6. python数据结构推荐书-「算法与数据结构」从入门到进阶吐血整理推荐书单

    推荐一下「算法与数据结构」从入门到进阶的书单. 一.入门系列 这些书籍通过图片.打比方等通俗易懂的方法来讲述,让你能达到懂一些基础算法,线性表,堆栈,队列,树,图,DP算法,背包问题等,不要求会实现, ...

  7. 【从蛋壳到满天飞】JS 数据结构解析和算法实现-AVL树(一)

    前言 [从蛋壳到满天飞]JS 数据结构解析和算法实现,全部文章大概的内容如下: Arrays(数组).Stacks(栈).Queues(队列).LinkedList(链表).Recursion(递归思 ...

  8. 【从蛋壳到满天飞】JS 数据结构解析和算法实现-哈希表

    前言 [从蛋壳到满天飞]JS 数据结构解析和算法实现,全部文章大概的内容如下: Arrays(数组).Stacks(栈).Queues(队列).LinkedList(链表).Recursion(递归思 ...

  9. (十五)算法设计思想之“回溯算法”

    算法设计思想之"回溯算法" 回溯算法是什么? 什么问题适合用回溯算法解决? 适合回溯算法解决的问题 全排列 LeetCode:46.全排列 LeetCode:78.子集 思考题 回 ...

  10. 【从蛋壳到满天飞】JS 数据结构解析和算法实现-堆和优先队列(一)

    前言 [从蛋壳到满天飞]JS 数据结构解析和算法实现,全部文章大概的内容如下: Arrays(数组).Stacks(栈).Queues(队列).LinkedList(链表).Recursion(递归思 ...

最新文章

  1. matPlotLib绘制决策树
  2. 如果打回车来更新模板列中的TEXTBOX
  3. php 后退按钮事件,php – 后退按钮的会话问题
  4. react循环的值为什么要有key_react中为何推荐设置key
  5. (转) 淘淘商城系列——Redis的安装
  6. Ensemble Learning
  7. MySQL orzdba、dodba、top、iostat、vmstat、perf等
  8. 如何为新建网站选择好的域名
  9. Winform 视频流叠加透明控件. 使用DSkin皮肤框架实现
  10. 网站制作流程详解(学做网站第一步)
  11. 仿ios相机apk_iCamera摄像头下载|iCamera仿苹果相机安卓版v4.0下载 _当游网
  12. 2022-2027年中国缝制机械行业市场全景评估及发展战略规划报告
  13. 安卓模拟器安装教程_无限多开仙境传说RO!第一安卓模拟器BlueStacks蓝叠安卓模拟器多开教程...
  14. 华硕飞行堡垒7按Fn+F5电脑显示屏不显示风扇模式
  15. 文件的后缀名怎样重命名,重命名为大写字母
  16. 利用python如何抓取微博评论?
  17. Linux文件查看命令
  18. B站台湾大学郭彦甫|MATLAB 学习笔记|06 高阶绘图 Advanced Plot
  19. 内核网络协议栈offload功能盘点
  20. 2022北京冬奥会开幕式里的黑科技,闪耀闪耀全世界

热门文章

  1. .net core学习
  2. C#下如何实现服务器 + 客户端的聊天程序
  3. windows10 C盘清理
  4. 宝塔面板 mongodb 允许外网访问
  5. 我的虚拟化设想(My virtualization vision)
  6. C语言给出任意4个数算24点,讨论24点算法。
  7. matlab绘制贝叶斯曲线,Matlab建立SVM,KNN和朴素贝叶斯模型分类绘制ROC曲线
  8. C++如何产生随机数
  9. mvc框架异常处理机制
  10. dataGridview与下拉框高级绑定