A*算法

A*算法详解(转)

第一部分:A*算法简介
    写这篇文章的初衷是应一个网友的要求,当然我也发现现在有关人工智能的中文站点实在太少,我在这里 抛砖引玉,希望大家都来热心的参与。 
    还是说正题,我先拿A*算法开刀,是因为A*在游戏中有它很典型的用法,是人工智能在游戏中的代表。 
    A*算法在人工智能中是一种典型的启发式搜索算法,为了说清楚A*算法,我看还是先说说何谓启发式算法。 

一、何谓启发式搜索算法:  

    在说它之前先提提状态空间搜索。状态空间搜索,如果按专业点的说法就是将问题求解过程表现为从 初始状态到目标状态寻找这个路径的过程。通俗点说,就是在解一个问题时,找到一条解题的过程可以从 求解的开始到问题的结果(好象并不通俗哦)。由于求解问题的过程中分枝有很多,主要是求解过程中求 解条件的不确定性,不完备性造成的,使得求解的路径很多这就构成了一个图,我们说这个图就是状态空 间。问题的求解实际上就是在这个图中找到一条路径可以从开始到结果。这个寻找的过程就是状态空间搜 索。  

    常用的状态空间搜索有深度优先和广度优先。广度优先是从初始状态一层一层向下找,直到找到目标 为止。深度优先是按照一定的顺序前查找完一个分支,再查找另一个分支,以至找到目标为止。这两种算 法在数据结构书中都有描述,可以参看这些书得到更详细的解释。 

    前面说的广度和深度优先搜索有一个很大的缺陷就是他们都是在一个给定的状态空间中穷举。这在状 态空间不大的情况下是很合适的算法,可是当状态空间十分大,且不预测的情况下就不可取了。他的效率 实在太低,甚至不可完成。在这里就要用到启发式搜索了。 

    启发式搜索就是在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置 进行搜索直到目标。这样可以省略大量无畏的搜索路径,提到了效率。在启发式搜索中,对位置的估价是 十分重要的。采用了不同的估价可以有不同的效果。我们先看看估价是如何表示的。 

    启发中的估价是用估价函数表示的,如: 

    f(n) = g(n) + h(n) 

    其中f(n) 是节点n的估价函数,g(n)实在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目 标节点最佳路径的估计代价。在这里主要是h(n)体现了搜索的启发信息,因为g(n)是已知的。如果说详细 点,g(n)代表了搜索的广度的优先趋势。但是当h(n) >> g(n)时,可以省略g(n),而提高效率。这些就深了, 不懂也不影响啦!我们继续看看何谓A*算法。 

二、初识A*算法: 

    启发式搜索其实有很多的算法,比如:局部择优搜索法、最好优先搜索法等等。当然A*也是。这些算法 都使用了启发函数,但在具体的选取最佳搜索节点时的策略不同。象局部择优搜索法,就是在搜索的过程中 选取“最佳节点”后舍弃其他的兄弟节点,父亲节点,而一直得搜索下去。这种搜索的结果很明显,由于舍 弃了其他的节点,可能也把最好的节点都舍弃了,因为求解的最佳节点只是在该阶段的最佳并不一定是全局 的最佳。最好优先就聪明多了,他在搜索时,便没有舍弃节点(除非该节点是死节点),在每一步的估价中 都把当前的节点和以前的节点的估价值比较得到一个“最佳的节点”。这样可以有效的防止“最佳节点”的 丢失。那么A*算法又是一种什么样的算法呢?其实A*算法也是一种最好优先的算法。只不过要加上一些约束 条件罢了。由于在一些问题求解时,我们希望能够求解出状态空间搜索的最短路径,也就是用最快的方法求 解问题,A*就是干这种事情的!我们先下个定义,如果一个估价函数可以找出最短的路径,我们称之为可采 纳性。A*算法是一个可采纳的最好优先算法。A*算法的估价函数克表示为: 

    f’(n) = g’(n) + h’(n) 

    这里,f’(n)是估价函数,g’(n)是起点到终点的最短路径值,h’(n)是n到目标的最断路经的启发值。由 于这个f’(n)其实是无法预先知道的,所以我们用前面的估价函数f(n)做近似。g(n)代替g’(n),但 g(n)>=g’(n) 才可(大多数情况下都是满足的,可以不用考虑),h(n)代替h’(n),但h(n)<=h’(n)才可(这一点特别的重 要)。可以证明应用这样的估价函数是可以找到最短路径的,也就是可采纳的。我们说应用这种估价函数的 最好优先算法就是A*算法。哈!你懂了吗?肯定没懂!接着看! 

    举一个例子,其实广度优先算法就是A*算法的特例。其中g(n)是节点所在的层数,h(n)=0,这种h(n)肯 定小于h’(n),所以由前述可知广度优先算法是一种可采纳的。实际也是。当然它是一种最臭的A*算法。 

    再说一个问题,就是有关h(n)启发函数的信息性。h(n)的信息性通俗点说其实就是在估计一个节点的值 时的约束条件,如果信息越多或约束条件越多则排除的节点就越多,估价函数越好或说这个算法越好。这就 是为什么广度优先算法的那么臭的原因了,谁叫它的h(n)=0,一点启发信息都没有。但在游戏开发中由于实 时性的问题,h(n)的信息越多,它的计算量就越大,耗费的时间就越多。就应该适当的减小h(n)的信息,即 减小约束条件。但算法的准确性就差了,这里就有一个平衡的问题。可难了,这就看你的了!  

    好了我的话也说得差不多了,我想你肯定是一头的雾水了,其实这是写给懂A*算法的同志看的。哈哈! 你还是找一本人工智能的书仔细看看吧!我这几百字是不足以将A*算法讲清楚的。只是起到抛砖引玉的作用 希望大家热情参与吗! 

    预知A*算法的应用,请看《深入A*算法》。

第二部分:深入A*算法———浅析A*算法在搜索最短路径中的应用

一、前言 

    在这里我将对A*算法的实际应用进行一定的探讨,并且举一个有关A*算法在最短路径搜索 的例子。值得注意的是这里并不对A*的基本的概念作介绍,如果你还对A*算法不清楚的话, 请看姊妹篇《初识A*算法》。 

    这里所举的例子是参考AMIT主页中的一个源程序,你可以在AMIT的站点上下载也可以在我 的站点上下载。你使用这个源程序时,应该遵守一定的公约。 

二、A*算法的程序编写原理 

    我在《初识A*算法》中说过,A*算法是最好优先算法的一种。只是有一些约束条件而已。 我们先来看看最好优先算法是如何编写的吧。 

    如图有如下的状态空间:(起始位置是A,目标位置是P,字母后的数字表示节点的估价值) 

    搜索过程中设置两个表:OPEN和CLOSED。OPEN表保存了所有已生成而未考察的节点,CLOSED 表中记录已访问过的节点。算法中有一步是根据估价函数重排OPEN表。这样循环中的每一 步只考虑OPEN表中状态最好的节点。具体搜索过程如下: 

    1)初始状态: 
    OPEN=[A5];CLOSED=[]; 
    2)估算A5,取得搜有子节点,并放入OPEN表中; 
    OPEN=[B4,C4,D6];CLOSED=[A5] 
    3)估算B4,取得搜有子节点,并放入OPEN表中; 
    OPEN=[C4,E5,F5,D6];CLOSED=[B4,A5] 
    4)估算C4;取得搜有子节点,并放入OPEN表中; 
    OPEN=[H3,G4,E5,F5,D6];CLOSED=[C4,B4,A5] 
    5)估算H3,取得搜有子节点,并放入OPEN表中; 
    OPEN=[O2,P3,G4,E5,F5,D6];CLOSED=H3C4,B4,A5] 
    6)估算O2,取得搜有子节点,并放入OPEN表中; 
    OPEN=[P3,G4,E5,F5,D6];CLOSED=[O2,H3,C4,B4,A5] 
    7)估算P3,已得到解; 
    看了具体的过程,再看看伪程序吧。算法的伪程序如下: 

Best_First_Search()
{
Open = [起始节点]; Closed = [];
while ( Open表非空 )
{
从Open中取得一个节点X,并从OPEN表中删除。
if (X是目标节点)
{
求得路径PATH;返回路径PATH;
}
for (每一个X的子节点Y)
{
if( Y不在OPEN表和CLOSE表中 )
{
求Y的估价值;并将Y插入OPEN表中;//还没有排序
}
else
if( Y在OPEN表中 )
{
if( Y的估价值小于OPEN表的估价值 )
更新OPEN表中的估价值;
}
else //Y在CLOSE表中
{
if( Y的估价值小于CLOSE表的估价值 )
{
更新CLOSE表中的估价值;
从CLOSE表中移出节点,并放入OPEN表中;
}
}
将X节点插入CLOSE表中;
按照估价值将OPEN表中的节点排序;
}//end for
}//end while
}//end func


      啊!伪程序出来了,写一个源程序应该不是问题了,依葫芦画瓢就可以。A*算法的程序与此  是一样的,只要注意估价函数中的g(n)的h(n)约束条件就可以了。不清楚的可以看看《初识A*算法》。好了,我们可以进入另一个重要的话题,用A*算法实现最短路径的搜索。在此之  前你最好认真的理解前面的算法。不清楚可以找我。我的Email在文章尾。 

三、用A*算法实现最短路径的搜索 

      在游戏设计中,经常要涉及到最短路径的搜索,现在一个比较好的方法就是用A*算法进行设  计。他的好处我们就不用管了,反正就是好!^_* 


      注意下面所说的都是以  ClassAstar  这个程序为蓝本,你可以在这里下载这个程序。这个程  序是一个完整的工程。里面带了一个EXE文件。可以先看看。 

      先复习一下,A*算法的核心是估价函数f(n),它包括g(n)和h(n)两部分。g(n)  是已经走过的  代价,h(n)是n到目标的估计代价。在这个例子中g(n)表示在状态空间从起始节点到  n节点的  深度,h(n)表示n节点所在地图的位置到目标位置的直线距离。啊!一个是状态空间,一个是  实际的地图,不要搞错了。再详细点说,有一个物体A,在地图上的坐标是(xa,ya),A所要到  达的目标b的坐标是(xb,yb)。则开始搜索时,设置一个起始节点1,生成八个子节点2  -  9  因  为有八个方向。如图: 

      仔细看看节点1、9、17的g(n)和h(n)是怎么计算的。现在应该知道了下面程序中的f(n)是如何  计算的吧。开始讲解源程序了。其实这个程序是一个很典型的教科书似的程序,也就是说只要  你看懂了上面的伪程序,这个程序是十分容易理解的。不过他和上面的伪程序有一些的不同,  我在后面会提出来。 

先看搜索主函数: 

void AstarPathfinder::FindPath(int sx, int sy, int dx, int dy)
{
NODE *Node, *BestNode;
int TileNumDest;
//得到目标位置,作判断用
TileNumDest = TileNum(sx, sy);
//生成Open和Closed表
OPEN=( NODE* )calloc(1,sizeof( NODE ));
CLOSED=( NODE* )calloc(1,sizeof( NODE ));
//生成起始节点,并放入Open表中
Node=( NODE* )calloc(1,sizeof( NODE ));
Node->g = 0;
//这是计算h值
Node->h = (dx-sx)*(dx-sx) + (dy-sy)*(dy-sy); // should really use sqrt().
//这是计算f值,即估价值
Node->f = Node->g+Node->h;
Node->NodeNum = TileNum(dx, dy);
Node->x = dx;
Node->y = dy;
OPEN->NextNode=Node; // make Open List point to first node
for (;;)
{ //从Open表中取得一个估价值最好的节点
BestNode=ReturnBestNode();
//如果该节点是目标节点就退出
if (BestNode->NodeNum == TileNumDest) // if we’ve found the end, break and finish
break;
//否则生成子节点
GenerateSuccessors(BestNode,sx,sy);
}
PATH = BestNode;
}
再看看生成子节点函数 GenerateSuccessors: 
void AstarPathfinder::GenerateSuccessors(NODE *BestNode, int dx, int dy)
{
int x, y;
//哦!依次生成八个方向的子节点,简单!
// Upper-Left
if ( FreeTile(x=BestNode->x-TILESIZE, y=BestNode->y-TILESIZE) )
GenerateSucc(BestNode,x,y,dx,dy);
// Upper
if ( FreeTile(x=BestNode->x, y=BestNode->y-TILESIZE) )
GenerateSucc(BestNode,x,y,dx,dy);
// Upper-Right
if ( FreeTile(x=BestNode->x+TILESIZE, y=BestNode->y-TILESIZE) )
GenerateSucc(BestNode,x,y,dx,dy);
// Right
if ( FreeTile(x=BestNode->x+TILESIZE, y=BestNode->y) )
GenerateSucc(BestNode,x,y,dx,dy);
// Lower-Right
if ( FreeTile(x=BestNode->x+TILESIZE, y=BestNode->y+TILESIZE) )
GenerateSucc(BestNode,x,y,dx,dy);
// Lower
if ( FreeTile(x=BestNode->x, y=BestNode->y+TILESIZE) )
GenerateSucc(BestNode,x,y,dx,dy);
// Lower-Left
if ( FreeTile(x=BestNode->x-TILESIZE, y=BestNode->y+TILESIZE) )
GenerateSucc(BestNode,x,y,dx,dy);
// Left
if ( FreeTile(x=BestNode->x-TILESIZE, y=BestNode->y) )
GenerateSucc(BestNode,x,y,dx,dy);
}

看看最重要的函数GenerateSucc: 
void AstarPathfinder::GenerateSucc(NODE *BestNode,int x, int y, int dx, int dy)
{
int g, TileNumS, c = 0;
NODE *Old, *Successor;
//计算子节点的 g 值
g = BestNode->g+1; // g(Successor)=g(BestNode)+cost of getting from BestNode to Successor
TileNumS = TileNum(x,y); // identification purposes
//子节点再Open表中吗?
if ( (Old=CheckOPEN(TileNumS)) != NULL ) // if equal to NULL then not in OPEN list, else it returns the Node in Old
{
//若在
for( c = 0; c <8; c++) if( BestNode->Child[c] == NULL ) // Add Old to the list of BestNode’s Children (or Successors).
break;
BestNode->Child[c] = Old;
//比较Open表中的估价值和当前的估价值(只要比较g值就可以了)
if ( g g ) // if our new g value is Parent = BestNode;
Old->g = g;
Old->f = g + Old->h;
}
}
else //在Closed表中吗?
if ( (Old=CheckCLOSED(TileNumS)) != NULL ) // if equal to NULL then not in OPEN list, else it returns the Node in Old
{
//若在
for( c = 0; c<8; c++) if ( BestNode->Child[c] == NULL ) // Add Old to the list of BestNode’s Children (or Successors).
break;
BestNode->Child[c] = Old;
//比较Closed表中的估价值和当前的估价值(只要比较g值就可以了)
if ( g g ) // if our new g value is Parent = BestNode;
Old->g = g;
Old->f = g + Old->h;
//再依次更新Old的所有子节点的估价值
PropagateDown(Old); // Since we changed the g value of Old, we need
// to propagate this new value downwards, i.e.
// do a Depth-First traversal of the tree!
}
}
else//不在Open表中也不在Close表中
{
//生成新的节点
Successor = ( NODE* )calloc(1,sizeof( NODE ));
Successor->Parent = BestNode;
Successor->g = g;
Successor->h = (x-dx)*(x-dx) + (y-dy)*(y-dy); // should do sqrt(), but since we don’t really
Successor->f = g+Successor->h; // care about the distance but just which branch looks
Successor->x = x; // better this should suffice. Anyayz it’s faster.
Successor->y = y;
Successor->NodeNum = TileNumS;
//再插入Open表中,同时排序。
Insert(Successor); // Insert Successor on OPEN list wrt f
for( c =0; c <8; c++) if ( BestNode->Child[c] == NULL ) // Add Old to the list of BestNode’s Children (or Successors).
break;
BestNode->Child[c] = Successor;
}
}



      哈哈!A*算法我懂了!当然,我希望你有这样的感觉!不过我还要再说几句。仔细看看这个程  序,你会发现,这个程序和我前面说的伪程序有一些不同,在GenerateSucc函数中,当子节点  在Closed表中时,没有将子节点从Closed表中删除并放入Open表中。而是直接的重新的计算该  节点的所有子节点的估价值(用PropagateDown函数)。这样可以快一些!另当子节点在  Open  表和Closed表中时,重新的计算估价值后,没有重新的对Open表中的节点排序,我有些想不通,  为什么不排呢?:-(,会不会是一个小小的BUG。你知道告诉我好吗
 
续:

经典算法研究系列:一、A*搜索算法              

                     
作者:July、二零一一年一月

更多请参阅:十三个经典算法研究与总结、目录+索引。
----------------------------------
博主说明:
1、本经典算法研究系列,此系列文章写的不够好之处,还望见谅。
2、本经典算法研究系列,系我参考资料,一篇一篇原创所作,转载必须注明作者本人July及出处。
3、本经典算法研究系列,精益求精,不断优化,永久更新,永久勘误。

欢迎,各位,与我一同学习探讨,交流研究。
有误之处,不吝指正。
-------------------------------------------

引言
    1968年,的一篇论文,“P. E. Hart, N. J. Nilsson, and B. Raphael. A formal basis for the heuristic determination of minimum cost paths in graphs. IEEE Trans. Syst. Sci. and Cybernetics, SSC-4(2):100-107, 1968”。从此,一种精巧、高效的算法------A*算法横空出世了,并在相关领域得到了广泛的应用。


启发式搜索算法
    要理解A*搜寻算法,还得从启发式搜索算法开始谈起。
    所谓启发式搜索,就在于当前搜索结点往下选择下一步结点时,可以通过一个启发函数来进行选择,选择代价最少的结点作为下一步搜索结点而跳转其上(遇到有一个以上代价最少的结点,不妨选距离当前搜索点最近一次展开的搜索点进行下一步搜索)。

    DFS和BFS在展开子结点时均属于盲目型搜索,也就是说,它不会选择哪个结点在下一次搜索中更优而去跳转到该结点进行下一步的搜索。在运气不好的情形中,均需要试探完整个解集空间, 显然,只能适用于问题规模不大的搜索问题中。

    而与DFS,BFS不同的是,一个经过仔细设计的启发函数,往往在很快的时间内就可得到一个搜索问题的最优解,对于NP问题,亦可在多项式时间内得到一个较优解。是的,关键就是如何设计这个启发函数。


A*搜寻算法
    A*搜寻算法,俗称A星算法,作为启发式搜索算法中的一种,这是一种在图形平面上,有多个节点的路径,求出最低通过成本的算法。常用于游戏中的NPC的移动计算,或线上游戏的BOT的移动计算上。该算法像Dijkstra算法一样,可以找到一条最短路径;也像BFS一样,进行启发式的搜索。

    A*算法最为核心的部分,就在于它的一个估值函数的设计上:
        f(n)=g(n)+h(n)

    其中f(n)是每个可能试探点的估值,它有两部分组成:
    一部分,为g(n),它表示从起始搜索点到当前点的代价(通常用某结点在搜索树中的深度来表示)。
    另一部分,即h(n),它表示启发式搜索中最为重要的一部分,即当前结点到目标结点的估值,
    h(n)设计的好坏,直接影响着具有此种启发式函数的启发式算法的是否能称为A*算法。

   一种具有f(n)=g(n)+h(n)策略的启发式算法能成为A*算法的充分条件是:
      1、搜索树上存在着从起始点到终了点的最优路径。
      2、问题域是有限的。
      3、所有结点的子结点的搜索代价值>0。
      4、h(n)=

    当此四个条件都满足时,一个具有f(n)=g(n)+h(n)策略的启发式算法能成为A*算法,并一定能找到最优解。

    对于一个搜索问题,显然,条件1,2,3都是很容易满足的,而条件4: h(n)<=h*(n)是需要精心设计的,由于h*(n)显然是无法知道的,所以,一个满足条件4的启发策略h(n)就来的难能可贵了。

    不过,对于图的最优路径搜索和八数码问题,有些相关策略h(n)不仅很好理解,而且已经在理论上证明是满足条件4的,从而为这个算法的推广起到了决定性的作用。

    且h(n)距离h*(n)的呈度不能过大,否则h(n)就没有过强的区分能力,算法效率并不会很高。对一个好的h(n)的评价是:h(n)在h*(n)的下界之下,并且尽量接近h*(n)。  

 

A*搜寻算法的实现 
      先举一个小小的例子:即求V0->V5的路径,V0->V5的过程中,可以经由V1,V2,V3,V4各点达到目的点V5。下面的问题,即是求此起始顶点V0->途径任意顶点V1、V2、V3、V4->目标顶点V5的最短路径。

//图片是引用rickone 的。上图中,说白了,无非就是:一个队列,open 往close 插入元素
           通过上图,我们可以看出::A*算法最为核心的过程,就在每次选择下一个当前搜索点时,是从所有已探知的但未搜索过点中(可能是不同层,亦可不在同一条支路上),选取f值最小的结点进行展开。
      而所有“已探知的但未搜索过点”可以通过一个按f值升序的队列(即优先队列)进行排列。
      这样,在整体的搜索过程中,只要按照类似广度优先的算法框架,从优先队列中弹出队首元素(f值),对其可能子结点计算g、h和f值,直到优先队列为空(无解)或找到终止点为止。

      A*算法与广度、深度优先和Dijkstra 算法的联系就在于:当g(n)=0时,该算法类似于DFS,当h(n)=0时,该算法类似于BFS。且同时,如果h(n)为0,只需求出g(n),即求出起点到任意顶点n的最短路径,则转化为单源最短路径问题,即Dijkstra算法。这一点,可以通过上面的A*搜索树的具体过程中将h(n)设为0或将g(n)设为0而得到。 

A*算法流程:
    首先将起始结点S放入OPEN表,CLOSE表置空,算法开始时:
      1、如果OPEN表不为空,从表头取一个结点n,如果为空算法失败。
      2、n是目标解吗?是,找到一个解(继续寻找,或终止算法)。
      3、将n的所有后继结点展开,就是从n可以直接关联的结点(子结点),如果不在CLOSE表中,就将它们放入OPEN表,并把S放入CLOSE表,同时计算每一个后继结点的估价值f(n),将OPEN表按f(x)排序,最小的放在表头,重复算法,回到1。

//OPEN-->CLOSE,起点-->任意顶点g(n)-->目标顶点h(n)
closedset := the empty set                
//已经被估算的节点集合  
    openset := set containing the initial node
//将要被估算的节点集合
    g_score[start] := 0                       
//g(n)
    h_score[start] := heuristic_estimate_of_distance(start, goal)   
//h(n)
    f_score[start] := h_score[start]    
     
    while openset is not empty    //若OPEN表不为空
        x := the node in openset having the lowest f_score[] value
//x为OPEN表中最小的
        if x = goal                                              
//如果x是一个解
            return reconstruct_path(came_from,goal)            
//
        remove x from openset
        add x to closedset                            //x放入

CLSOE表
        for each y in neighbor_nodes(x)
            if y in closedset
                continue
            tentative_g_score := g_score[x] + dist_between(x,y)

            if y not in openset
                add y to openset
                tentative_is_better := true
            else if tentative_g_score < g_score[y]
                tentative_is_better := true
            else
                tentative_is_better := false
            if tentative_is_better = true
                came_from[y] := x
                g_score[y] := tentative_g_score
                h_score[y] := heuristic_estimate_of_distance(y, goal) 
//x-->y-->goal
                f_score[y] := g_score[y] + h_score[y]
    return failure

function reconstruct_path(came_from,current_node)
    if came_from[current_node] is set
        p = reconstruct_path(came_from,came_from[current_node])
        return (p + current_node)
    else
        return the empty path 

     与结点写在一起的数值表示那个结点的价值f(n),当OPEN表为空时CLOSE表中将求得从V0到其它所有结点的最短路径。

     考虑到算法性能,外循环中每次从OPEN表取一个元素,共取了n次(共n个结点),每次展开一个结点的后续结点时,需O(n)次,同时再对OPEN表做一次排序,OPEN表大小是O(n)量级的,若用快排就是O(nlogn),乘以外循环总的复杂度是O(n^2 * logn),

     如果每次不是对OPEN表进行排序,因为总是不断地有新的结点添加进来,所以不用进行排序,而是每次从OPEN表中求一个最小的,那只需要O(n)的复杂度,所以总的复杂度为O(n*n),这相当于Dijkstra算法。

 本文完。
    
July、二零一一年二月十日更新。
------------------------------------------------

后续:July、二零一一年三月一日更新。
简述A*最短路径算法的方法:
   目标:从当前位置A到目标位置B找到一条最短的行走路径。

   方法:从A点开始,遍历所有的可走路径,记录到一个结构中,记录内容为(位置点,最小步数)
         当任何第二次走到一个点的时候,判断最小步骤是否小于记录的内容,如果是,则更新掉原最小步数,一直到所有的路径点都不能继续都了为止,最终那个点被标注的最小步数既是最短路径,
         而反向找跟它相连的步数相继少一个值的点连起来就形成了最短路径,当多个点相同,则任意取一条即可。

   总结:
   A*算法实际是个穷举算法,也与课本上教的最短路径算法类似。课本上教的是两头往中间走,也是所有路径都走一次,每一个点标注最短值。(更多,请参考此A*搜寻算法的后续一篇文章:
一(续)、A*,Dijkstra,BFS算法性能比较及A*算法的应用谢谢大家。)本文完。

你可能感兴趣的:(A算法)