学习笔记:A*算法

简易地图         

如图所示简易地图, 其中绿色方块的是起点 (用 A 表示), 中间蓝色的是障碍物, 红色的方块 (用 B 表示) 是目的地. 为了可以用一个二维数组来表示地图, 我们将地图划分成一个个的小方块.

二维数组在游戏中的应用是很多的, 比如贪吃蛇和俄罗斯方块基本原理就是移动方块而已. 而大型游戏的地图, 则是将各种"地貌"铺在这样的小方块上.

寻路步骤          

1. 从起点A开始, 把它作为待处理的方格存入一个"开启列表", 开启列表就是一个等待检查方格的列表.

2. 寻找起点A周围可以到达的方格, 将它们放入"开启列表", 并设置它们的"父方格"为A.

3. 从"开启列表"中删除起点 A, 并将起点 A 加入"关闭列表", "关闭列表"中存放的都是不需要再次检查的方格

图中浅绿色描边的方块表示已经加入 "开启列表" 等待检查. 淡蓝色描边的起点 A 表示已经放入 "关闭列表" , 它不需要再执行检查.

从 "开启列表" 中找出相对最靠谱的方块, 什么是最靠谱? 它们通过公式 F=G+H 来计算.

        F = G + H

G 表示从起点 A 移动到网格上指定方格的移动耗费 (可沿斜方向移动).

H 表示从指定的方格移动到终点 B 的预计耗费 (H 有很多计算方法, 这里我们设定只可以上下左右移动).

我们假设横向移动一个格子的耗费为10, 为了便于计算, 沿斜方向移动一个格子耗费是14. 为了更直观的展示如何运算 FGH, 图中方块的左上角数字表示 F, 左下角表示 G, 右下角表示 H. 看看是否跟你心里想的结果一样?

从 "开启列表" 中选择 F 值最低的方格 C (绿色起始方块 A 右边的方块), 然后对它进行如下处理:

4. 把它从 "开启列表" 中删除, 并放到 "关闭列表" 中.

5. 检查它所有相邻并且可以到达 (障碍物和 "关闭列表" 的方格都不考虑) 的方格. 如果这些方格还不在 "开启列表" 里的话, 将它们加入 "开启列表", 计算这些方格的 G, H 和 F 值各是多少, 并设置它们的 "父方格" 为 C.

6. 如果某个相邻方格 D 已经在 "开启列表" 里了, 检查如果用新的路径 (就是经过C 的路径) 到达它的话, G值是否会更低一些, 如果新的G值更低, 那就把它的 "父方格" 改为目前选中的方格 C, 然后重新计算它的 F 值和 G 值 (H 值不需要重新计算, 因为对于每个方块, H 值是不变的). 如果新的 G 值比较高, 就说明经过 C 再到达 D 不是一个明智的选择, 因为它需要更远的路, 这时我们什么也不做.

如图, 我们选中了 C 因为它的 F 值最小, 我们把它从 "开启列表" 中删除, 并把它加入 "关闭列表". 它右边上下三个都是墙, 所以不考虑它们. 它左边是起始方块, 已经加入到 "关闭列表" 了, 也不考虑. 所以它周围的候选方块就只剩下 4 个. 让我们来看看 C 下面的那个格子, 它目前的 G 是14, 如果通过 C 到达它的话, G将会是 10 + 10, 这比 14 要大, 因此我们什么也不做.

然后我们继续从 "开启列表" 中找出 F 值最小的, 但我们发现 C 上面的和下面的同时为 54, 这时怎么办呢? 这时随便取哪一个都行, 比如我们选择了 C 下面的那个方块 D.

D 右边已经右上方的都是墙, 所以不考虑, 但为什么右下角的没有被加进 "开启列表" 呢? 因为如果 C 下面的那块也不可以走, 想要到达 C 右下角的方块就需要从 "方块的角" 走了, 在程序中设置是否允许这样走. (图中的示例不允许这样走)

就这样, 我们从 "开启列表" 找出 F 值最小的, 将它从 "开启列表" 中移掉, 添加到 "关闭列表". 再继续找出它周围可以到达的方块, 如此循环下去...

那么什么时候停止呢? —— 当我们发现 "开始列表" 里出现了目标终点方块的时候, 说明路径已经被找到.

如何找回路径       

如上图所示, 除了起始方块, 每一个曾经或者现在还在 "开启列表" 里的方块, 它都有一个 "父方块", 通过 "父方块" 可以索引到最初的 "起始方块", 这就是路径.

将整个过程抽象      

把起始格添加到 "开启列表"
do

{

寻找开启列表中F值最低的格子, 我们称它为当前格.

把它切换到关闭列表.

对当前格相邻的8格中的每一个

if (它不可通过 || 已经在 "关闭列表" 中)

{

什么也不做.

}

if (它不在开启列表中)

{

把它添加进 "开启列表", 把当前格作为这一格的父节点, 计算这一格的 FGH

if (它已经在开启列表中)

{

if (用G值为参考检查新的路径是否更好, 更低的G值意味着更好的路径)

{

把这一格的父节点改成当前格, 并且重新计算这一格的 GF 值.

}

} while( 目标格已经在 "开启列表", 这时候路径被找到)

如果开启列表已经空了, 说明路径不存在.

最后从目标格开始, 沿着每一格的父节点移动直到回到起始格, 这就是路径.

时间: 2024-11-01 18:28:09

学习笔记:A*算法的相关文章

算法学习笔记 KMP算法之 next 数组详解

最近回顾了下字符串匹配 KMP 算法,相对于朴素匹配算法,KMP算法核心改进就在于:待匹配串指针 i 不发生回溯,模式串指针 j 跳转到 next[j],即变为了 j = next[j]. 由此时间复杂度由朴素匹配的 O(m*n) 降到了 O(m+n), 其中模式串长度 m, 待匹配文本串长 n. 其中,比较难理解的地方就是 next 数组的求法.next 数组的含义:代表当前字符之前的字符串中,有多大长度的相同前缀后缀,也可看作有限状态自动机的状态,而且从自动机的角度反而更容易推导一些. "前

Machine Learning In Action 第二章学习笔记: kNN算法

本文主要记录<Machine Learning In Action>中第二章的内容.书中以两个具体实例来介绍kNN(k nearest neighbors),分别是: 约会对象预测 手写数字识别 通过“约会对象”功能,基本能够了解到kNN算法的工作原理.“手写数字识别”与“约会对象预测”使用完全一样的算法代码,仅仅是数据集有变化. 约会对象预测 1 约会对象预测功能需求 主人公“张三”喜欢结交新朋友.“系统A”上面注册了很多类似于“张三”的用户,大家都想结交心朋友.“张三”最开始通过自己筛选的

[算法学习笔记]排序算法——堆排序

堆排序 堆排序(heapsort)也是一种相对高效的排序方法,堆排序的时间复杂度为O(n lgn),同时堆排序使用了一种名为堆的数据结构进行管理. 二叉堆 二叉堆是一种特殊的堆,二叉堆是完全二叉树或者是近似完全二叉树.二叉堆满足堆特性:父节点的键值总是保持固定的序关系于任何一个子节点的键值,且每个节点的左子树和右子树都是一个二叉堆. 如上图显示,(a)是一个二叉堆(最大堆), (b)是这个二叉堆在数组中的存储形式. 通过给个一个节点的下标i, 很容易计算出其父节点,左右子节点的的下标,为了方便,

STL学习笔记(算法概述)

算法头文件 要运用C++标准程序库的算法,首先必须包含头文件<algorithm> 使用STL算法时,经常需要用到仿函数以及函数配接器.它们定义域<functional>头文件中. 算法的分类 可以按以下分类方式描述各个STL算法: 非变动性算法(nonmodifying algorithms) 变动性算法(modifying algorithms) 移除性算法(removing algorithms) 变序性算法(mutating algorithms) 排序算法(sorting

ios学习笔记---排序算法

排序算法 1.概念 所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作.排序算法,就是如何使得记录按照要求排列的方法. 2.选择排序算法时常用的几个参照 a.稳定性 假定在带排序的记录序列中,存在多个具有相同关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri = rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的:否则称为不稳定的. b.时间复杂度 c.空间复杂度 3.算法 冒泡排序 选择排序 插入排序

Java学习笔记——排序算法之进阶排序(堆排序与分治并归排序)

春蚕到死丝方尽,蜡炬成灰泪始干 --无题 这里介绍两个比较难的算法: 1.堆排序 2.分治并归排序 先说堆. 这里请大家先自行了解完全二叉树的数据结构. 堆是完全二叉树.大顶堆是在堆中,任意双亲值都大于(或等于)其孩子值,就称其为大顶堆. 堆排序的步骤: 1.把数组想象成一个堆.数组的index+1就是其对应在堆中的序号 2.调堆中各值的顺序,得到大顶堆 3.将堆首位值与堆末尾值交换,最大值排序完毕 4.将堆得大小减1,重复步骤2和步骤3,直到堆中只剩下一个元素.排序完毕 上代码: 1 publ

【网络流】网络流学习笔记Part2ISAP算法

说实话ISAP的文献真的不太好找= =而且介绍的没有太详细,不像SAP Dinic比较普及. ISAP其实是改进的SAP算法,要学ISAP就先去看一下SAP好了.(事实上很多人会把ISAP和SAP搞混了.尤其在国内,很多人会直接管ISAP叫SAP) SAP算法(即Edmonds-Karp算法): 不断进行BFS找增广路径,那么最多找V*E次就一定不存在增广路径了. 时间复杂度 O(V*E^2) ISAP算法: 通过维护距离标号使得寻找增广路径的过程被简化从而提高效率.距离标号可以使某个点到汇点s

OPENCV学习笔记15_算法设计中使用策略模式

Building a bug-free(无BUG) application is just the beginning. What you really want is an application that you and the programmers working with you(团队) will be able to easily adapt and evolve (修改和升级)as new requirements come in(随着新的需求进入,面临新的需求). Basical

Java学习笔记——排序算法之希尔排序(Shell Sort)

落日楼头,断鸿声里,江南游子.把吴钩看了,栏杆拍遍,无人会,登临意. --水龙吟·登建康赏心亭 希尔算法是希尔(D.L.Shell)于1959年提出的一种排序算法.是第一个时间复杂度突破O(n2)的算法之一. 其基础是插入排序. 上代码: 1 public class ShellSort { 2 3 public static void shellSort(int[] arr){ 4 5 int increment = arr.length; 6 int temp;//牌 7 int i; 8

学习笔记 ST算法

[引子]RMQ (Range Minimum/Maximum Query)问题: 对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题. {方法} 1.朴素(即搜索),O(n)-O(qn) online. 2.线段树,O(n)-O(qlogn) online. 3.ST(实质是动态规划),O(nlogn)-O(q) online. ST算法(Sparse Table),以求最大值为例,设d[i,