赞
踩
文章出自:http://dsqiu.iteye.com/blog/1689130
图遍历算法——DFS、BFS、A*、B*和Flood Fill 遍历算法大串讲
本文内容框架:
§1 图遍历DFS和BFS两种实现
§2 A*算法
§3 B*算法
§4 Flood Fill算法
§5 小结
图遍历问题分为四类:
遍历完所有的边而不能有重复,即所謂“一笔画问题”或“欧拉路径”;
遍历完所有的顶点而没有重复,即所谓“哈密尔顿问题”。
遍历完所有的边而可以有重复,即所谓“中国邮递员问题”;
遍历完所有的顶点而可以重复,即所谓“旅行推销员问题”。
对于第一和第三类问题已经得到了完满的解决,而第二和第四类问题则只得到了部分解决。
第一类问题就是研究所谓的欧拉图的性质,而第二类问题则是研究所谓的哈密尔顿图的性质。
§1 图遍历DFS和BFS两种实现
图遍历的矩阵存储和邻接表存储的BFS和DFS实现
╔
矩阵存储BFS
链表存储BFS
矩阵存储DFS
链表存储BFS
╝①
§2 A*算法
A*算法
A*算法是一种常用的启发式搜索算法。
╔
在A*算法中,一个结点位置的好坏用估价函数来对它进行评估。A*算法的估价函数可表示为:
f'(n) = g'(n) + h'(n)
这里,f'(n)是估价函数,g'(n)是起点到终点的最短路径值(也称为最小耗费或最小代价),h'(n)是n到目标的最短路经的启发值。由于这个f'(n)其实是无法预先知道的,所以实际上使用的是下面的估价函数:
f(n) = g(n) + h(n)
其中g(n)是从初始结点到节点n的实际代价,h(n)是从结点n到目标结点的最佳路径的估计代价。在这里主要是h(n)体现了搜索的启发信息,因为g(n)是已知的。用f(n)作为f'(n)的近似,也就是用g(n)代替g'(n),h(n)代替h'(n)。这样必须满足两个条件:(1)g(n)>=g'(n)(大多数情况下都是满足的,可以不用考虑),且f必须保持单调递增。(2)h必须小于等于实际的从当前节点到达目标节点的最小耗费h(n)<=h'(n)。第二点特别的重要。可以证明应用这样的估价函数是可以找到最短路径的。
A*算法的具体步骤
A*算法基本上与广度优先算法相同,但是在扩展出一个结点后,要计算它的估价函数,并根据估价函数对待扩展的结点排序,从而保证每次扩展的结点都是估价函数最小的结点。
1)建立一个队列,计算初始结点的估价函数f,并将初始结点入队,设置队列头和尾指针。
2)取出队列头(队列头指针所指)的结点,如果该结点是目标结点,则输出路径,程序结束。否则对结点进行扩展。
3)检查扩展出的新结点是否与队列中的结点重复,若与不能再扩展的结点重复(位于队列头指针之前),则将它抛弃;若新结点与待扩展的结点重复(位于队列头指针之后),则比较两个结点的估价函数中g的大小,保留较小g值的结点。跳至第五步。
4)如果扩展出的新结点与队列中的结点不重复,则按照它的估价函数f大小将它插入队列中的头结点后待扩展结点的适当位置,使它们按从小到大的顺序排列,最后更新队列尾指针。
5)如果队列头的结点还可以扩展,直接返回第二步。否则将队列头指针指向下一结点,再返回第二步。
╝②
A*算法图解
╔
如图所示简易地图, 其中绿色方块的是起点 (用 A 表示), 中间蓝色的是障碍物, 红色的方块 (用 B 表示) 是目的地. 为了可以用一个二维数组来表示地图, 我们将地图划分成一个个的小方块。
二维数组在游戏中的应用是很多的, 比如贪吃蛇和俄罗斯方块基本原理就是移动方块而已. 而大型游戏的地图, 则是将各种"地貌"铺在这样的小方块上。
F = G + H,
G 表示从起点 A 移动到网格上指定方格的移动耗费 (可沿斜方向移动)。
H 表示从指定的方格移动到终点 B 的预计耗费 (H 有很多计算方法, 这里我们设定只可以上下左右移动)。
我们假设横向移动一个格子的耗费为10, 为了便于计算, 沿斜方向移动一个格子耗费是14. 为了更直观的展示如何运算 F,G,H, 图中方块的左上角数字表示 F, 左下角表示 G, 右下角表示 H。 看看上图是否跟你心里想的结果一样?
将上图A周围的方块全部放入队列,取出F值最小的方块C,看看 C 下面的那个格子, 它目前的 G 是14, 如果通过 C 到达它的话, G将会是 10 + 10, 这比 14 要大, 因此我们什么也不做(上面步骤3))。
以C为中心结点,扩展新结点 ,并进入队列(上面步骤4))。
直到最终找到终点B,上面浅蓝色边缘的方块是移动过的地点(实际走过的地方),浅绿色边缘的方块是还在队列中的方块。
╝③
A*算法实现
╔
╝④
B*算法
╔
B* 寻路算法又叫Branch Star 分支寻路算法,且与A*对应,本算法适用于游戏中怪物的自动寻路,其效率远远超过A*算法,经过测试,效率是普通A*算法的几十上百倍。
通过引入该算法,一定程度上解决了游戏服务器端无法进行常规寻路的效率问题,除非服务器端有独立的AI处理线程,否则在服务器端无法允许可能消耗大量时间的寻路搜索,即使是业界普遍公认的最佳的A*,所以普遍的折中做法是服务器端只做近距离的寻路,或通过导航站点缩短A*的范围。
§3 B*算法
B*算法原理
本算法启发于自然界中真实动物的寻路过程,并加以改善以解决各种阻挡问题(有点类似蚁群算法)。
前置定义:
1、探索节点:
为了叙述方便,我们定义在寻路过程中向前探索的节点(地图格子)称为探索节点,起始探索节点即为原点。(探索节点可以对应为A*中的开放节点)。
2、自由的探索节点:
探索节点朝着目标前进,如果前方不是阻挡,探索节点可以继续向前进入下一个地图格子,这种探索节点我们称为自由探索节点;
3、绕爬的探索节点:
探索节点朝着目标前进,如果前方是阻挡,探索节点将试图绕过阻挡,绕行中的探索节点我们成为绕爬的探索节点;
B*算法流程
1、起始,探索节点为自由节点,从原点出发,向目标前进;
2、自由节点前进过程中判断前面是否为障碍,
a、不是障碍,向目标前进一步,仍为自由节点;
b、是障碍,以前方障碍为界,分出左右两个分支,分别试图绕过障碍,这两个分支节点即成为两个绕爬的探索节点;
3、绕爬的探索节点绕过障碍后,又成为自由节点,回到2);
4、探索节点前进后,判断当前地图格子是否为目标格子,如果是则寻路成功,根据寻路过程构造完整路径;
5、寻路过程中,如果探索节点没有了,则寻路结束,表明没有目标格子不可达;
B*算法图解演示
B*算法与A*算法的性能比较
寻路次数比较(5秒钟寻路次数)
╝⑤
§4 Flood Fill算法
Flood Fill算法
Flood Fill算法是计算机图形学和数字图像处理的一个填充算法,其实就是从一点开始向四面周围寻找点填充遍历,原理和BFS很相似,当然也可以像DFS一样的遍历。
Flood Fill 算法图解演示
Flood Fill算法实现
说的Flood Fill算法的实现不得不提Lode's Computer Graphics Tutorial。该算法可以通过递归或者是stack来完成,下面只附上4-Way Recurisive Method:
§5 小结
这篇文章摘录了图算法最基本的BFS和DFS的实现以及A*、B*和Flood Fill的基本原理,由于原理不是十分难懂又有图解过程,所以可以一次性掌握原理(虽然文字介绍相当简要,不过好像也没有什么要说的),剩下的动手的问题。如果你有任何建议或者批评和补充,请留言指出,不胜感激,更多参考请移步互联网。
参考:
①MemoryGarden:http://www.cppblog.com/MemoryGarden/articles/97979.html
②阿凡卢:http://www.cnblogs.com/luxiaoxun/archive/2012/08/05/2624115.html
③ Create Chen: http://www.cnblogs.com/technology/archive/2011/05/26/2058842.html
④在迷茫中寻找四叶草 --MulinB的技术博客:http://blog.csdn.net/mulinb/article/details/5939225
⑤inysong:http://qinysong.iteye.com/blog/678941
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。