在A寻路中使用二叉堆
2009-10-15 11:57:46 来源:WEB开发网往堆中添加新元素
当我们实际在寻路算法中使用二叉堆的时候,还需要考虑更多,但是现在我们只是学习一下如何使用二叉堆。我跳过这部分以便更容易理解基本的东西。我会在文章后面的部分给出处理这一切的完整公式,但了解这些细节仍然十分重要。
大致的,为了往堆里添加元素,我们把它放在数组的末尾。然后和它在 当前位置/2 处的父节点比较,分数部分被圆整。如果新元素的F值更低,我们就交换这两个元素。然后我们比较这个元素和它的新父节点,在 (当前位置)/2 ,小数部分圆整,的地方。如果它的F值更低,我们再次交换。我们重复这个过程直到这个元素不再比它的父节点低,或者这个元素已经到达顶端,处于数组的位置1。
我们来看如何把一个F值为17的元素添加到已经存在的堆中。我们的堆里现在有7个元素,新元素将被添加到第8个位置。这就是堆看起来的样子,新元素被加了下划线。10 30 20 34 38 30 24 17
接下来我们比较它和它的父节点,在 8/2 也就是 4的位置上。位置4当前元素的F值是34。既然17比34低,我们交换两元素的位置。现在我们的堆看起来是这样的:
10 30 20 17 38 30 24 34
然后我们把它和新的父节点比较。因为我们在位置4,我们就把它和 4/2 = 2 这个位置上的元素比较。那个元素的F值是30。因为17比30低,我们再次交换,现在堆看起来是这样的:
10 17 20 30 38 30 24 34
接着我们比较它和新的父节点。现在我们在第二个位置,我们把它和 2/2 = 1,也就是堆顶端的比较。这次,17不比10更低,我们停止,堆保持成现在的样子。
从堆中删除元素
从堆中删除元素是个类似的过程,但是差不多是反过来的。首先,我们删除位置1的元素,现在它空了。然后,我们取堆的最后一个元素,移动到位置1。在堆中,这是结束的条件。以前的末元素被加了下划线。
34 17 20 30 38 30 24
然后我们比较它和两个子节点,它们分别在位置(当前位置*2)和(当前位置* 2 + 1)。如果它比两个子节点的F值都低,就保持原位。反之,就把它和较低的子节点交换。那么,在这里,该元素的两个子节点的位置在 1 * 2 = 2和 1*2 + 1 = 3。显然,34不比任何一个子节点低,所以我们把它和较低的子节点,也就是17,交换。结果看起来是这样:
17 34 20 30 38 30 24
接着我们把它和新的子节点比较,它们在 2*2 = 4,和2*2 + 1 = 5的位置上。它不比任何一个子节点低,所以我们把它和较低的一个子节点交换(位置4上的30)。现在是这样:
17 30 20 34 38 30 24
最后一次,我们比较它和新的子节点。照例,子节点在位置 4*2 = 8和4*2+1 = 9的位置上。但是那些位置上并没有元素,因为列表没那么长。我们已经到达了堆的底端,所以我们停下来。
二叉堆为什么这么快?
现在你知道了堆基本的插入和删除方法,你应该明白为什么它比其他方法,比如说插入排序更快。假设你有个有1000个节点的开启列表,在一格有很多节点的相当大的地图上,这不是不可能(记住,即使是100×100的地图,上面也有10,000个节点)。如果你使用插入排序,从起点开始,到找到新元素恰当的位置,在把新元素插入之前,平均需要做500次比较。
使用二叉堆,你从底端开始,可能只要1-3次比较就能把新元素插入到正确的位置。你还需要9次比较用来从开启列表中移除一个元素,同时保持堆仍然有序。在A*中,你通常每次只需要移除一个元素(F值最低的元素),在任意位置添加0到5个新节点(就像主文章里描述的2D寻路)。这总共花费的时间大约是同样数量节点进行插入排序的1%。差别随你地图的增大(也就是节点更多)呈几何增长。地图越小,就越没优势,这也是为什么你的地图和节点越少,二叉堆的价值就越低的原因。
顺便,使用二叉堆并不意味着你的寻路算法会快100倍。在下面还讲了一些棘手的问题。额外的,A*不仅仅是为开启列表排序。然而,根据我的经验,用二叉堆在大部分场合可以提高2-3倍的速度,更长的路径,速度提高的更多。
创建开启列表数组
现在我们了解了二叉堆,那么如何使用它呢?首先要做的是构造我们的一维数组。为此,我们先要确定它的大小。一般来说,列表大小不会超过地图上的节点总数(在最坏的情况下,我们搜索整个地图寻找路径)。在一个方形二维地图中,就如我的主文章中描述的,我们的节点不超过 地图宽 × 地图高。那么我们的一维数组就是那个大小。在这个例子里,我们叫这个数组 openList()。堆最顶端的元素被存储在openList(1),第二个元素在openList(2),依此类推。
使用指针
现在我们有了正确大小的数组,几乎可以开始用来寻路了。不过,在进一步的添加或者删除操作之前,我们再看看原始的堆。
- ››使用脚本恢复WinXP系统的用户登录密码
- ››使用phpMyadmin创建数据库及独立数据库帐号
- ››使用Zend Framework框架中的Zend_Mail模块发送邮件...
- ››使用cout标准输出如何控制小数点后位数
- ››使用nofollow标签做SEO的技巧
- ››使用 WebSphere Message Broker 的 WebSphere Tra...
- ››使用SQL Server事件探查器做应用程序的性能分析
- ››使用SQL Server事件探查器分析死锁原因
- ››使用纯文本文件打造WCF服务
- ››使用 Dojo 开发定制 Business Space 小部件,第 4...
- ››使用 ADDRESS 与 INDIRECT函数查询信息
- ››使用 COLUMN函数编制单元信息
更多精彩
赞助商链接