A*算法原理

广度优先搜索和深度优先搜索都属于盲目型搜索算法,在选择下一个搜索节点的时候没有一个准则,去选择最优的。很多情况下需要会穷举整个空间,因此只适用于规模较小的搜索。因此需要用到另外一种算法---启发式搜索。


一、A*算法原理

启发式搜索就是在状态空间中对每一个搜索位置进行评估,得到最好的位置,再从这个位置进行搜素直到目标。这样可以省略大量无谓的搜索路径。在启发式搜索中,对位置的估计尤其重要,采用不同的估计函数会有不同的效果。

启发式搜索中的估价函数为:

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

f(n):节点n的估价函数。

g(n):从起始点到n节点的实际代价。

h(n):从节点n到目标节点最佳路径的估计代价。

在这里,h(n)便是估价函数,体现了搜索的启发信息。g(n)是已知的。

启发式搜索有很多算法:局部最优搜索、最优优先搜索算法等等。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)才可(这一点特别的重要),即小于n到目标的实际代价值。可以证明应用这样的估价函数是可以找到最短路径的,也就是可采纳的。我们说应用这种估价函数的最好优先算法就是A*算法,否则就不是A*算法。在这种情况下,搜索的范围大,效率低,但是能得到最优解。如果估价值>实际代价,搜索范围小,效率高,但是不能保证得到最优解。

注意:估价值与实际代价值越接近,估价函数选取得就越好。


关于h(n)启发函数的信息性的问题


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


关于启发函数h(n)的相容性


如果h(n)满足h(n1) - h(n2) <= c(n1,n2),其中c(n1,n2)是n1节点到n2节点的实际代价,则称h(n)是相容的。也就是说状态转移时,下界h的减小值最多等于状态转移的实际代价,其实就是要求h不能减小得太快。

那么,对于节点n1以及其后继节点n2,有:

f(n1) = g(n1) + h(n1)

f(n2) = g(n2) + h(n2)

又由于:

g(n2) = g(n1) + c(n1,n2),推导出:

f(n2) = g(n1) + c(n1,n2) + h(n2)

如果h(n)是相容的,则有h(n1) <= h(n2) + c(n1,n2),两边同时加上g(n1),推导出:

h(n1) + g(n1) <= h(n2) + g(n2) + c(n1,n2),也就是:

f(n1) <= f(n2)

 可以看出,如果启发函数h(n)相容,那么估价函数f(n)单调非递减。就是说按照此算法搜索最先生成的路径一定是最短的。这样的话,搜索过程中就不再需要维持一个CLOSED表,只需维护一个已访问节点表OPEN表。

在选择启发函数的时候,最好选择相容的启发式函数。


A星算法步骤:

1,把起始格添加到开启列表。
   2,重复如下的工作:
      a) 寻找开启列表中F值最低的格子。我们称它为当前格。
      b) 把它切换到关闭列表。
      c) 对相邻的8格中的每一个?
          * 如果它不可通过或者已经在关闭列表中,略过它。反之如下。
          * 如果它不在开启列表中,把它添加进去。把当前格作为这一格的父节点。记录这一格的F,G,和H值。
          * 如果它已经在开启列表中,用G值为参考检查新的路径是否更好。更低的G值意味着更好的路径。如果是这样,就把这一格的父节点改成当前格,并且重新计算这一格的G和F值。如果你保持你的开启列表按F值排序,改变之后你可能需要重新对开启列表排序。
      d) 停止,当你
          * 把目标格添加进了关闭列表(注解),这时候路径被找到,或者
          * 没有找到目标格,开启列表已经空了。这时候,路径不存在。
   3.保存路径。从目标格开始,沿着每一格的父节点移动直到回到起始格。这就是你的路径。


二、A*算法编程实现

本文通过迷宫寻路实例,实现了A*算法的C++类封装。

迷宫地图:


起始点是绿色方格,目标点是景色方格,蓝色方格表示障碍物,不可通过。

在搜索过程中,横向移动或者纵向移动的代价都是10,对角线上的移动代价是14.需要注意的是:障碍物的角落不可通过。

将迷宫信息写在一个文本文件里面:


第一行的两个整数代表迷宫的行数和列数。第二行是起始点和目标点的坐标(x,y)。x是所在的行,y是所在的列(均以0开始计算)。

接下来便是迷宫的具体信息,0表示可以通过,1表示障碍物。


下面贴出代码,如有错误,欢迎批评指正(邮箱:xiajunhust@gmail.com)。

程序代码(C++):

AStarPathFinding.h:

  1. #ifndef ASTARPATHFINDING_H   
  2. #define ASTARPATHFINDING_H   
  3.   
  4. #include <queue>//为了使用优先级队列priority_queue   
  5. #include <stack>   
  6. #include <vector>   
  7.   
  8. //迷宫地图中节点类型标记   
  9. enum{  
  10.     NODE_EMPTY,//可以通过的节点   
  11.     NODE_OBSTACLE,//障碍物,不可通过   
  12.     NODE_PATH//路径上的点   
  13. };  
  14.   
  15. //记录路径上的点的坐标   
  16. typedef struct tagpathNode{  
  17.     int x,y;  
  18. }PathNode;  
  19.   
  20. //节点数据结构定义   
  21. typedef struct tagNode{  
  22.     int x,y;//当前点在迷宫中的位置坐标   
  23.     int g;//起始点到当前点实际代价   
  24.     int h;//当前节点到目标节点最佳路径的估计代价   
  25.     int f;//估计函数:f = g + h。   
  26.     struct tagNode *father;//指向其父节点的指针   
  27. }Node;  
  28.   
  29. //定义STL优先队列的排序方式   
  30. class HeapCompare_f{  
  31. public:  
  32.     bool operator()(Node* x,Node* y) const  
  33.     {  
  34.         return x->f > y->f;//依据估价函数进行排序:升序排列   
  35.     }  
  36. };  
  37.   
  38. //迷宫寻路:A*算法   
  39. class AStarPathFinding{  
  40. public:  
  41.   
  42. private:  
  43.     char *m_mapFileName;//存储地图信息的文件名   
  44.     int m_rows,m_cols;//迷宫的高度和宽度   
  45.     int **m_maze;//迷宫布局   
  46.     int m_startX,m_startY;//起始点坐标   
  47.     int m_endX,m_endY;//目标点坐标   
  48.     int dx[8],dy[8];//8个子节点移动方向:上、下、左、右、左上、右上、右下、左下   
  49.   
  50.     Node *startNode,*endNode;//起始节点和目标节点   
  51.     int **m_path;//记录路径信息   
  52.     int m_steps;//搜索所花费的总步数   
  53.   
  54.     //OPEN表:采用C++ STL中vector实现优先级队列功能   
  55.     //注意:存储的是Node*指针   
  56.     std::vector<Node*> OPENTable;  
  57.     //CLOSED表:存储的也是Node*指针   
  58.     std::vector<Node*> CLOSEDTable;  
  59.   
  60. public:  
  61.     //构造函数   
  62.     AStarPathFinding(char* mapFileName);  
  63.     ~AStarPathFinding();//析构函数   
  64.     void init();//初始化   
  65.     //读取地图信息   
  66.     bool readMap();  
  67.     //寻路主函数   
  68.     bool pathFinding();  
  69.     //产生路径信息   
  70.     void generatePath();  
  71.     //打印路径信息   
  72.     void printPath();  
  73.     //估计当前点到目标点的距离:曼哈顿距离   
  74.     int judge(int x,int y);  
  75.     //判断某一节点是否合法   
  76.     bool isIllegle(int x,int y);  
  77. };  
  78.   
  79. #endif  


AStarPathFnding.cpp:

  1. #include "AStarPathFinding.h"   
  2.   
  3. #include <iostream>   
  4. #include <cstdio>   
  5. #include <cmath>   
  6. #include <string>   
  7. #include <fstream>   
  8.   
  9. using namespace std;  
  10.   
  11. const int MaxDistance = 9999;  
  12.   
  13. AStarPathFinding::AStarPathFinding(char* mapFileName)  
  14. :m_steps(0)  
  15. {  
  16.     m_mapFileName = (char *)malloc((strlen(mapFileName) + 1) * sizeof(char));  
  17.     strcpy(m_mapFileName,mapFileName);  
  18. }  
  19.   
  20. AStarPathFinding::~AStarPathFinding()  
  21. {  
  22.     free(m_mapFileName);  
  23.   
  24.     //千万不能有这句代码,因为startNode已加入OPEN表,会在释放OPEN表   
  25.     //的时候释放,否则会造成重复释放,出现bug   
  26.     //delete startNode;   
  27.     delete endNode;  
  28.   
  29.     释放迷宫布局数组:注意多维数组空间释放   
  30.     for (int i = 0;i < m_rows;++i)  
  31.     {  
  32.         delete[] m_maze[i];  
  33.     }  
  34.     delete[] m_maze;  
  35.   
  36.     for (int i = 0;i < m_rows;++i)  
  37.     {  
  38.         delete[] m_path[i];  
  39.     }  
  40.     delete[] m_path;  
  41.   
  42.     //释放OPEN表以及CLOSED表内存空间   
  43.     vector<Node*>::iterator iter;  
  44.     for (iter = OPENTable.begin();iter != OPENTable.end();++iter)  
  45.     {  
  46.         delete (*iter);  
  47.     }  
  48.     OPENTable.clear();  
  49.   
  50.     vector<Node*>::iterator iter2;  
  51.     for (iter2 = CLOSEDTable.begin();iter2 != CLOSEDTable.end();++iter2)  
  52.     {  
  53.         delete (*iter2);  
  54.     }  
  55.     CLOSEDTable.clear();  
  56. }  
  57.   
  58. void AStarPathFinding::init()  
  59. {  
  60.     dx[0] =dx[4] = dx[5] = -1;  
  61.     dx[1] =dx[3] = 0;  
  62.     dx[2] =dx[6] = dx[7] = 1;  
  63.   
  64.     dy[3] = dy[4] = dy[7] = -1;  
  65.     dy[0] =dy[2] = 0;  
  66.     dy[1] =dy[5] = dy[6] = 1;  
  67.   
  68.     readMap();  
  69.   
  70.     //分配空间   
  71.     m_path = new int *[m_rows];  
  72.     for (int i = 0;i < m_rows;++i)  
  73.     {  
  74.         m_path[i] = new int[m_cols];  
  75.     }  
  76.   
  77.     startNode = new Node;  
  78.     startNode->x = m_startX;  
  79.     startNode->y = m_startY;  
  80.     startNode->g = 0;  
  81.     startNode->h = judge(startNode->x,startNode->y);  
  82.     startNode->f = startNode->g + startNode->h;  
  83.     startNode->father = NULL;  
  84.   
  85.     endNode = new Node;  
  86.     endNode->x = m_endX;  
  87.     endNode->y = m_endY;  
  88.     endNode->father = NULL;  
  89. }  
  90.   
  91. bool AStarPathFinding::pathFinding()  
  92. {  
  93.     //判断起始点和目标点是否是同一点   
  94.     if (m_startX == m_endX && m_startY == m_endY)  
  95.     {  
  96.         cout << "WARNNING : The start point is the same as th destination " << endl;  
  97.         return true;  
  98.     }  
  99.   
  100.     OPENTable.push_back(startNode);//起始点装入OPEN表   
  101.   
  102.     //对vector中元素进行排序:将最后一个元素加入原本已序的heap内   
  103.     push_heap(OPENTable.begin(),OPENTable.end(),HeapCompare_f());  
  104.   
  105.     Node *tempNode = new Node;  
  106.   
  107.     //开始遍历   
  108.     for (;;)  
  109.     {  
  110.         if (OPENTable.empty())//判断OPEN表是否为空   
  111.         {  
  112.             cout << "ERROR : unable to find the destination" << endl;  
  113.             return false;  
  114.         }  
  115.   
  116.         tempNode = OPENTable.front();//注意:OPEN表为空会导致未定义行为   
  117.         ++m_steps;  
  118.         //将第一个元素移到最后,并将剩余区间重新排序,组成新的heap   
  119.         pop_heap(OPENTable.begin(),OPENTable.end(),HeapCompare_f());  
  120.         OPENTable.pop_back();//删除最后一个元素   
  121.   
  122.         //判断是否已经搜寻到目标节点   
  123.         if (tempNode->x == m_endX && tempNode->y == m_endY)  
  124.         {  
  125.             cout << "OK : success to find the destination" << endl;  
  126.             endNode->g = tempNode->g;  
  127.             endNode->h = tempNode->h;  
  128.             endNode->f = tempNode->f;  
  129.             endNode->father = tempNode->father;  
  130.   
  131.             generatePath();  
  132.   
  133.             return true;  
  134.         }  
  135.           
  136.         for (int i = 0;i < 8;++i)//针对每个子节点   
  137.         {  
  138.             int nextX = tempNode->x + dx[i];  
  139.             int nextY = tempNode->y + dy[i];  
  140.             if (isIllegle(nextX,nextY))  
  141.             {  
  142.                 //注意:障碍物角落不能直接通过   
  143.                 if (1 == *(*(m_maze + tempNode->x) + nextY) ||  
  144.                     1 == *(*(m_maze + nextX) + tempNode->y))  
  145.                 {  
  146.                     continue;  
  147.                 }  
  148.                 //计算此子节点的g值   
  149.                 int newGVal;  
  150.                 if (!dx[i] && !dy[i])//位于对角线上   
  151.                 {  
  152.                     newGVal = tempNode->g + 14;  
  153.                 }  
  154.                 else  
  155.                     newGVal = tempNode->g + 10;  
  156.   
  157.                 //搜索OPEN表,判断此点是否在OPEN表中   
  158.                 vector<Node*>::iterator OPENTableResult;  
  159.                 for (OPENTableResult = OPENTable.begin();  
  160.                     OPENTableResult != OPENTable.end();++OPENTableResult)  
  161.                 {  
  162.                     if ((*OPENTableResult)->x == nextX &&  
  163.                         (*OPENTableResult)->y == nextY)  
  164.                     {  
  165.                         break;  
  166.                     }  
  167.                 }  
  168.   
  169.                 //此子节点已经存在于OPEN表中   
  170.                 if (OPENTableResult != OPENTable.end())  
  171.                 {  
  172.                     //OPEN表中节点的g值已经是最优的,则跳过此节点   
  173.                     if ((*OPENTableResult)->g <= newGVal)  
  174.                     {  
  175.                         continue;  
  176.                     }  
  177.                 }  
  178.   
  179.                 //搜索CLOSED表,判断此节点是否已经存在于其中   
  180.                 vector<Node*>::iterator CLOSEDTableResult;  
  181.                 for (CLOSEDTableResult = CLOSEDTable.begin();  
  182.                     CLOSEDTableResult != CLOSEDTable.end();++CLOSEDTableResult)  
  183.                 {  
  184.                     if ((*CLOSEDTableResult)->x == nextX &&  
  185.                         (*CLOSEDTableResult)->y == nextY)  
  186.                     {  
  187.                         break;  
  188.                     }  
  189.                 }  
  190.   
  191.                 //此节点已经存在于CLOSED表中   
  192.                 if (CLOSEDTableResult != CLOSEDTable.end())  
  193.                 {  
  194.                     //CLOSED表中的节点已经是最优的,则跳过   
  195.                     if ((*CLOSEDTableResult)->g <= newGVal)  
  196.                     {  
  197.                         continue;  
  198.                     }  
  199.                 }  
  200.   
  201.                 //此节点是迄今为止的最优节点   
  202.                 Node *bestNode = new Node;  
  203.                 bestNode->x = nextX;  
  204.                 bestNode->y = nextY;  
  205.                 bestNode->father = tempNode;  
  206.                 bestNode->g = newGVal;  
  207.                 bestNode->h = judge(nextX,nextY);  
  208.                 bestNode->f = bestNode->g + bestNode->h;  
  209.   
  210.                 //如果已经存在于CLOSED表中,将其移除   
  211.                 if (CLOSEDTableResult != CLOSEDTable.end())  
  212.                 {  
  213.                     delete (*CLOSEDTableResult);  
  214.                     CLOSEDTable.erase(CLOSEDTableResult);  
  215.                 }  
  216.   
  217.                 //如果已经存在于OPEN表,更新   
  218.                 if (OPENTableResult != OPENTable.end())  
  219.                 {  
  220.                     delete (*OPENTableResult);  
  221.                     OPENTable.erase(OPENTableResult);  
  222.   
  223.                     //重新建堆,实现排序。注意不能用sort_heap,因为如果容器为空的话会出现bug   
  224.                     make_heap(OPENTable.begin(),OPENTable.end(),HeapCompare_f());  
  225.                 }  
  226.   
  227.                 OPENTable.push_back(bestNode);//将最优节点放入OPEN表   
  228.   
  229.                 push_heap(OPENTable.begin(),OPENTable.end(),HeapCompare_f());//重新排序   
  230.             }  
  231.         }  
  232.   
  233.         CLOSEDTable.push_back(tempNode);  
  234.     }  
  235.   
  236.     return false;  
  237. }  
  238.   
  239. void AStarPathFinding::generatePath()  
  240. {  
  241.   
  242.     Node *nodeChild = endNode;  
  243.     Node *nodeParent = endNode->father;  
  244.     do   
  245.     {  
  246.         *(*(m_path + nodeChild->x) + nodeChild->y) = NODE_PATH;//标记为路径上的点   
  247.         nodeChild = nodeParent;  
  248.         nodeParent = nodeParent->father;  
  249.     } while (nodeChild != startNode);  
  250.   
  251.     *(*(m_path + startNode->x) + startNode->y) = NODE_PATH;//标记为路径上的点   
  252. }  
  253.   
  254. void AStarPathFinding::printPath()  
  255. {  
  256.     cout << "The path is " << endl;  
  257.     for (int i = 0;i < m_rows;++i)  
  258.     {  
  259.         for (int j = 0;j < m_cols;++j)  
  260.         {  
  261.             if (NODE_PATH == *(*(m_path + i) + j))  
  262.             {  
  263.                 cout << "# ";  
  264.             }  
  265.             else  
  266.                 cout << *(*(m_maze + i) + j) << " ";  
  267.         }  
  268.         cout << endl;  
  269.     }  
  270.   
  271.     cout << "搜索总步数:"  << m_steps << endl;  
  272. }  
  273.   
  274. bool AStarPathFinding::readMap()  
  275. {  
  276.     //从文本文件读取迷宫布局信息   
  277.     ifstream mapFileStream(m_mapFileName);  
  278.     if (!mapFileStream)  
  279.     {  
  280.         cerr << "ERROR : unable to open map file" << endl;  
  281.         return false;  
  282.     }  
  283.   
  284.     mapFileStream >> m_rows >> m_cols;  
  285.   
  286.     //多维数组空间分配   
  287.     m_maze = new int *[m_rows];  
  288.     for (int i = 0;i < m_rows;++i)  
  289.     {  
  290.         m_maze[i] = new int[m_cols];  
  291.     }  
  292.   
  293.     mapFileStream >> m_startX >> m_startY;  
  294.     mapFileStream >> m_endX >> m_endY;  
  295.   
  296.     for (int i = 0;i < m_rows;++i)  
  297.     {  
  298.         for (int j = 0;j < m_cols;++j)  
  299.         {  
  300.             mapFileStream >> *(*(m_maze + i) + j);  
  301.         }  
  302.     }  
  303.   
  304.     return true;  
  305. }  
  306.   
  307. int AStarPathFinding::judge(int x, int y)  
  308. {  
  309.     return (10 * (abs(m_endX - x) + abs(m_endY - y)));  
  310. }  
  311.   
  312. bool AStarPathFinding::isIllegle(int x, int y)  
  313. {  
  314.     if (x >= 0 && x < m_rows &&  
  315.         y >= 0 && y < m_cols &&  
  316.         *(*(m_maze + x) + y) == 0)  
  317.         return true;  
  318.     else  
  319.         return false;  
  320. }  
#include "AStarPathFinding.h"
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <fstream>
using namespace std;
const int MaxDistance = 9999;
AStarPathFinding::AStarPathFinding(char* mapFileName)
:m_steps(0)
{
 m_mapFileName = (char *)malloc((strlen(mapFileName) + 1) * sizeof(char));
 strcpy(m_mapFileName,mapFileName);
}
AStarPathFinding::~AStarPathFinding()
{
 free(m_mapFileName);
 //千万不能有这句代码,因为startNode已加入OPEN表,会在释放OPEN表
 //的时候释放,否则会造成重复释放,出现bug
 //delete startNode;
 delete endNode;
 释放迷宫布局数组:注意多维数组空间释放
 for (int i = 0;i < m_rows;++i)
 {
  delete[] m_maze[i];
 }
 delete[] m_maze;
 for (int i = 0;i < m_rows;++i)
 {
  delete[] m_path[i];
 }
 delete[] m_path;
 //释放OPEN表以及CLOSED表内存空间
 vector<Node*>::iterator iter;
 for (iter = OPENTable.begin();iter != OPENTable.end();++iter)
 {
  delete (*iter);
 }
 OPENTable.clear();
 vector<Node*>::iterator iter2;
 for (iter2 = CLOSEDTable.begin();iter2 != CLOSEDTable.end();++iter2)
 {
  delete (*iter2);
 }
 CLOSEDTable.clear();
}
void AStarPathFinding::init()
{
 dx[0] =dx[4] = dx[5] = -1;
 dx[1] =dx[3] = 0;
 dx[2] =dx[6] = dx[7] = 1;
 dy[3] = dy[4] = dy[7] = -1;
 dy[0] =dy[2] = 0;
 dy[1] =dy[5] = dy[6] = 1;
 readMap();
 //分配空间
 m_path = new int *[m_rows];
 for (int i = 0;i < m_rows;++i)
 {
  m_path[i] = new int[m_cols];
 }
 startNode = new Node;
 startNode->x = m_startX;
 startNode->y = m_startY;
 startNode->g = 0;
 startNode->h = judge(startNode->x,startNode->y);
 startNode->f = startNode->g + startNode->h;
 startNode->father = NULL;
 endNode = new Node;
 endNode->x = m_endX;
 endNode->y = m_endY;
 endNode->father = NULL;
}
bool AStarPathFinding::pathFinding()
{
 //判断起始点和目标点是否是同一点
 if (m_startX == m_endX && m_startY == m_endY)
 {
  cout << "WARNNING : The start point is the same as th destination " << endl;
  return true;
 }
 OPENTable.push_back(startNode);//起始点装入OPEN表
 //对vector中元素进行排序:将最后一个元素加入原本已序的heap内
 push_heap(OPENTable.begin(),OPENTable.end(),HeapCompare_f());
 Node *tempNode = new Node;
 //开始遍历
 for (;;)
 {
  if (OPENTable.empty())//判断OPEN表是否为空
  {
   cout << "ERROR : unable to find the destination" << endl;
   return false;
  }
  tempNode = OPENTable.front();//注意:OPEN表为空会导致未定义行为
  ++m_steps;
  //将第一个元素移到最后,并将剩余区间重新排序,组成新的heap
  pop_heap(OPENTable.begin(),OPENTable.end(),HeapCompare_f());
  OPENTable.pop_back();//删除最后一个元素
  //判断是否已经搜寻到目标节点
  if (tempNode->x == m_endX && tempNode->y == m_endY)
  {
   cout << "OK : success to find the destination" << endl;
   endNode->g = tempNode->g;
   endNode->h = tempNode->h;
   endNode->f = tempNode->f;
   endNode->father = tempNode->father;
   generatePath();
   return true;
  }
  
  for (int i = 0;i < 8;++i)//针对每个子节点
  {
   int nextX = tempNode->x + dx[i];
   int nextY = tempNode->y + dy[i];
   if (isIllegle(nextX,nextY))
   {
    //注意:障碍物角落不能直接通过
    if (1 == *(*(m_maze + tempNode->x) + nextY) ||
     1 == *(*(m_maze + nextX) + tempNode->y))
    {
     continue;
    }
    //计算此子节点的g值
    int newGVal;
    if (!dx[i] && !dy[i])//位于对角线上
    {
     newGVal = tempNode->g + 14;
    }
    else
     newGVal = tempNode->g + 10;
    //搜索OPEN表,判断此点是否在OPEN表中
    vector<Node*>::iterator OPENTableResult;
    for (OPENTableResult = OPENTable.begin();
     OPENTableResult != OPENTable.end();++OPENTableResult)
    {
     if ((*OPENTableResult)->x == nextX &&
      (*OPENTableResult)->y == nextY)
     {
      break;
     }
    }
    //此子节点已经存在于OPEN表中
    if (OPENTableResult != OPENTable.end())
    {
     //OPEN表中节点的g值已经是最优的,则跳过此节点
     if ((*OPENTableResult)->g <= newGVal)
     {
      continue;
     }
    }
    //搜索CLOSED表,判断此节点是否已经存在于其中
    vector<Node*>::iterator CLOSEDTableResult;
    for (CLOSEDTableResult = CLOSEDTable.begin();
     CLOSEDTableResult != CLOSEDTable.end();++CLOSEDTableResult)
    {
     if ((*CLOSEDTableResult)->x == nextX &&
      (*CLOSEDTableResult)->y == nextY)
     {
      break;
     }
    }
    //此节点已经存在于CLOSED表中
    if (CLOSEDTableResult != CLOSEDTable.end())
    {
     //CLOSED表中的节点已经是最优的,则跳过
     if ((*CLOSEDTableResult)->g <= newGVal)
     {
      continue;
     }
    }
    //此节点是迄今为止的最优节点
    Node *bestNode = new Node;
    bestNode->x = nextX;
    bestNode->y = nextY;
    bestNode->father = tempNode;
    bestNode->g = newGVal;
    bestNode->h = judge(nextX,nextY);
    bestNode->f = bestNode->g + bestNode->h;
    //如果已经存在于CLOSED表中,将其移除
    if (CLOSEDTableResult != CLOSEDTable.end())
    {
     delete (*CLOSEDTableResult);
     CLOSEDTable.erase(CLOSEDTableResult);
    }
    //如果已经存在于OPEN表,更新
    if (OPENTableResult != OPENTable.end())
    {
     delete (*OPENTableResult);
     OPENTable.erase(OPENTableResult);
     //重新建堆,实现排序。注意不能用sort_heap,因为如果容器为空的话会出现bug
     make_heap(OPENTable.begin(),OPENTable.end(),HeapCompare_f());
    }
    OPENTable.push_back(bestNode);//将最优节点放入OPEN表
    push_heap(OPENTable.begin(),OPENTable.end(),HeapCompare_f());//重新排序
   }
  }
  CLOSEDTable.push_back(tempNode);
 }
 return false;
}
void AStarPathFinding::generatePath()
{
 Node *nodeChild = endNode;
 Node *nodeParent = endNode->father;
 do 
 {
  *(*(m_path + nodeChild->x) + nodeChild->y) = NODE_PATH;//标记为路径上的点
  nodeChild = nodeParent;
  nodeParent = nodeParent->father;
 } while (nodeChild != startNode);
 *(*(m_path + startNode->x) + startNode->y) = NODE_PATH;//标记为路径上的点
}
void AStarPathFinding::printPath()
{
 cout << "The path is " << endl;
 for (int i = 0;i < m_rows;++i)
 {
  for (int j = 0;j < m_cols;++j)
  {
   if (NODE_PATH == *(*(m_path + i) + j))
   {
    cout << "# ";
   }
   else
    cout << *(*(m_maze + i) + j) << " ";
  }
  cout << endl;
 }
 cout << "搜索总步数:"  << m_steps << endl;
}
bool AStarPathFinding::readMap()
{
 //从文本文件读取迷宫布局信息
 ifstream mapFileStream(m_mapFileName);
 if (!mapFileStream)
 {
  cerr << "ERROR : unable to open map file" << endl;
  return false;
 }
 mapFileStream >> m_rows >> m_cols;
 //多维数组空间分配
 m_maze = new int *[m_rows];
 for (int i = 0;i < m_rows;++i)
 {
  m_maze[i] = new int[m_cols];
 }
 mapFileStream >> m_startX >> m_startY;
 mapFileStream >> m_endX >> m_endY;
 for (int i = 0;i < m_rows;++i)
 {
  for (int j = 0;j < m_cols;++j)
  {
   mapFileStream >> *(*(m_maze + i) + j);
  }
 }
 return true;
}
int AStarPathFinding::judge(int x, int y)
{
 return (10 * (abs(m_endX - x) + abs(m_endY - y)));
}
bool AStarPathFinding::isIllegle(int x, int y)
{
 if (x >= 0 && x < m_rows &&
  y >= 0 && y < m_cols &&
  *(*(m_maze + x) + y) == 0)
  return true;
 else
  return false;
}

针对上面的迷宫问题的搜索结果:

注意:'#'代表该节点是路径上的点。

  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
A*算法是一种启发式搜索算法,可以用于解决最短路径问题。它以启发式函数为辅助,通过评估每个节点到目标节点的距离来指导搜索方向,从而提高搜索效率。以下是A*算法的具体原理。 1. 初始化:将起始点加入开放列表,并将其估计值(启发式函数)f设为0。 2. 搜索过程:从开放列表中选取f值最小的节点进行扩展。将该节点从开放列表中删除,并将其加入到关闭列表中。对于该节点的相邻节点,如果相邻节点不在关闭列表中,则将其加入开放列表,并计算它的f值(启发式函数),同时记录该节点的父节点。 3. 判断终点:重复执行第2步,直到终点被加入到关闭列表中或者开放列表为空。 4. 搜索成功:如果终点被加入到关闭列表中,搜索成功,通过回溯每个节点的父节点,得到从起始点到终点的最短路径。 以下是A*算法的伪代码: ``` 1. Put the starting node s on a list called the open list. 2. Set the f-value of s to 0. 3. 4. while the open list is not empty: 5. Select the node n with the lowest f-value from the open list. 6. Remove n from the open list and add it to the closed list. 7. if n is the goal node: 8. return the path from s to n. 9. 10. for each successor of n: 11. if successor is not in the closed list: 12. if successor is not in the open list: 13. add successor to the open list. 14. set the f-value of successor to f(n) + g(n, successor). 15. set the parent of successor to n. 16. else if the f-value of n + g(n, successor) is less than the f-value of successor: 17. update the f-value of successor to f(n) + g(n, successor). 18. set the parent of successor to n. 19. 20. return "no path exists". ``` 其中,f(n)表示节点n的估价函数值,g(n, successor)表示从节点n到successor的实际代价。 以下是一个简单的例子,说明如何使用A*算法找到从源节点s到目标节点t的最短路径。 假设有以下有向图: ![A*算法示例图](https://i.imgur.com/w0YBmJj.png) 我们要找到从源节点s到目标节点t的最短路径。 1. 初始化:将起始节点s加入到开放列表,将f(s)设为0。 2. 第一轮扩展:从开放列表中选取f值最小的节点s进行扩展。将s从开放列表中删除,并将其加入到关闭列表中。对于s的相邻节点a和b,计算它们的f值,并记录它们的父节点为s,并将它们加入到开放列表中。 ``` f(a) = g(s, a) + h(a, t) = 3 + 2 = 5 f(b) = g(s, b) + h(b, t) = 2 + 4 = 6 ``` 此时开放列表为[a, b],关闭列表为[s]。 3. 第二轮扩展:从开放列表中选取f值最小的节点a进行扩展。将a从开放列表中删除,并将其加入到关闭列表中。对于a的相邻节点c和d,计算它们的f值,并记录它们的父节点为a,并将它们加入到开放列表中。 ``` f(c) = g(a, c) + h(c, t) = 1 + 3 = 4 f(d) = g(a, d) + h(d, t) = 3 + 2 = 5 ``` 此时开放列表为[b, c, d],关闭列表为[s, a]。 4. 第三轮扩展:从开放列表中选取f值最小的节点c进行扩展。将c从开放列表中删除,并将其加入到关闭列表中。对于c的相邻节点t,计算它的f值,并记录它的父节点为c,并将其加入到开放列表中。 ``` f(t) = g(c, t) + h(t, t) = 2 + 0 = 2 ``` 此时开放列表为[b, d, t],关闭列表为[s, a, c]。 5. 第四轮扩展:从开放列表中选取f值最小的节点t进行扩展。将t从开放列表中删除,并将其加入到关闭列表中。搜索成功,通过回溯每个节点的父节点,得到从起始点s到目标节点t的最短路径为s->a->d->t。 6. 搜索成功,得到最短路径为s->a->d->t。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值