http://blog.csdn.net/shanshanpt/article/details/8977512

关于A*算法,很早就想写点什么,可是貌似天天在忙活着什么,可事实又没有做什么,真是浮躁啊!所以今晚还是来写一下总结吧!

A*算法是很经典的只能启发式搜索算法,关于只能搜索算法和一般的搜索算法(例如DFS,BFS之类),在语言描述上的区别,我觉得用《代码大全》中的一句话描述的非常好:“驾驶汽车达到某人家,写成算法是:沿167号高速往南行至Puyallup,从XX出口后往山上开4.5英里,在一个杂货店旁边的红绿灯右转,接着在第一个路口左转,从左边的褐色大房子的车道进去就是;而启发式是:找出上一次我们给你寄的信,照着信上地址开车到这个镇,到了那里你问一下我们房子在那里,这里的每一个人都认识我们,肯定会有人愿意帮助你,如果你找不到人,就找一个电话亭打电话给我们,我们出来接你”。听上去还是有点抽象。那么下面我们具体看看A*算法!

注:本文的图形以及一些思想借助于http://www.policyalmanac.org/games/aStarTutorial.htm,非常感谢经典文   章!

我们要想从绿色的点到红色的点,需要启发式得去找一条路径,到底怎么去找呢,开始没有办法,只能从开始点慢慢尝试!我们需要定义一个OPEN表,OPEN表中放的是什么呢?就是当前考虑的点,及其周边的点需要添加进来,作为可能的路径上的点。这样说可能有点抽象,那么先看下面:

我们从起始点开始搜索:

1:从点A开始,并且把它作为待处理点存入一个OPEN表。你的路径可能会通过它包含的方格,也可能不会。基本上,这是一个待检查方格的列表。
   2:寻找起点周围(邻居)所有可到达或者可通过的方格,如果有障碍物方格。那么就不需要考虑他们,对于其他的吕剧节点也把他们加入OPEN表。这些邻居节点认当前点为父节点。(父节点的保存是必须的!因为当我们找到最后一个点的时候,需要通过父节点回溯,找到所有点直到开始节点!那么就是一个完整的路径!)
   3:从开启列表中删除点A,把它加入到一个CLOSE列表,列表中保存所有不需要再次检查的方格。

在这一点,你应该形成如图的结构。在图中,暗绿色方格是你起始方格的中心。它被用浅蓝色描边,以表示它被加入到关闭列表中了。所有的相邻格现在都在开启列表中,它们被用浅绿色描边。每个方格都有一个灰色指针反指他们的父方格,也就是开始的方格。

结下来我们应该选择哪一个点继续考虑呢!我想大家应该知道所谓的启发函数,所谓权值之类(此处所谓的权值就是路劲的长度)。YES,我们需要OPEN表中权值F最小的那个点!为什么呢,当然是权值越小,越靠近目标点咯!

对于权值我们设为F,那么F怎么计算到的!我们有两个项!G和H,

G = 从起点A,沿着产生的路径,移动到网格上指定方格的路径耗费。
H = 从网格上那个方格移动到终点B的预估移动耗费。这经常被称为启发式的。这样叫的原因是因为它只是个猜测。我们没办法事先知道路径的长度。(但是我们需要知道:虽然只是猜测,但是只要是基于一个统一的标准,相对远近的趋势是不变的!这一点是很重要的! )

例如:H值的估计采用“曼哈顿”法,也就是当前的点,到目标点,横向和纵向的格子数相加,就是H值!

( 注意:对于障碍物我们是不考虑是否跳过的问题!即,障碍物也当做正常的一个格子!这也是H值仅仅是预测的而已的原因!即所谓启发式! )

那么对于第一幅图,起始点离终点的H值是:横向相差4个格子,纵向相差0个格子,那么H=4+0=4;

当然也有其他的办法,例如使用直线距离,sqrt( pow( des.x - src.x ) + pow( des.y - src.y ) )也是可以的~

对于G值!在这个例子,令水平或者垂直移动的耗费为10,对角线方向耗费为14。我们取这些值是因为沿对角线的距离是沿水平或垂直移动耗费的的根号2约1.414倍。为了简化,我们用10和14近似。有时候简化是很重要的~

( 其实距离只要反应了基本的倍数关系就可以了! )

对于起始点以及周围点的G值,H值和F值,下图可以很清晰的看到!( 左上角是F,左下角是G,右下角是H )

对于G值,只要是横向和竖向的邻居,设为+10,斜向的邻居+14就可以了~计算真的很easy吧~呵呵~

对于H值,就是数格子就是咯~

F = G + H

注意上面的邻居节点都加入OPEN表了哦~~~   起点从OPEN中删除,加入CLOSE中~

接着计算:

然后从OPEN表中选择一个F值最小的然后考虑其周围邻居,再循环处理!直到终点加入CLOSE中,寻路结束!或者OPEN空了,没找到路径!

( 至于我们怎么选出最小的那个点呢!?我们使用堆排序处理的,对于选出最小值是很快的~ )

可以看到,F最小的是起始点右边的那个点,下面框框表示的~

然后再考虑其邻居:

此时对于其邻居有几种可能性

1:邻居已经在CLOSE表中了,那么不需要考虑了~

2:邻居是障碍物,不需要考虑了e

3:邻居不在OPEN表中,那么将邻居加入OPEN,并将次邻居的父节点赋值为当前节点

4:邻居在OPEN中,那么需要看看此邻居点的G值与当前点的(G值+当前点到邻居点的距离(10或者14))的大小,如果从此点走更近(即G值更小),那么将此点的父节点换成当前点!(注意:对于同一个点,G值下,那么F必须小!因为H是相同的!)

下面是进一步的图示:

那么我们按照上面的思路,知道终点加入CLOSE表!( 终极图示如下 )

最终我们可以得到路径:( 之前我们说过,由父节点往前回溯就很easy的得到路径了~ )

说了这么多,也不知道说的行不行,还是需要总结一下!

总结:

1:将起始点加入OPEN表中

2:循环直到OPEN为空或者终点加入CLOSE表中

否则:找到OPEN表中F值最小的节点(使用堆排序得到小值点),将此点从OPEN删除,加入CLOSE!

(此时最小点已经取出,那么需要从新排序OPEN表,是的第一个点是最小F的点!)

对8个邻居进行处理:

若:1:邻居已经在CLOSE表中了,那么不需要考虑了~

2:邻居是障碍物,不需要考虑了e

3:邻居不在OPEN表中,那么将邻居加入OPEN,并将次邻居的父节点赋值为当前节点

4:邻居在OPEN中,那么需要看看此邻居点的G值与当前点的(G值+当前点到邻居点的距

离 (10或者14))的大小,如果从此点走更近(即G值更小),那么将此点的父节点换成当前           点!  (注意:对于同一个点,G值下,那么F必须小!因为H是相同的!)

  注意:当父节点改变后,OPEN表中的最小点可能会变化,那么需要再次排序得到最

        小点!

3:结束,根据退出循环的条件可以知道到底有没有找到路径!所以下面的工作就easy了~

基本的原理就是这样的~

下面给一个简单的C语言的演示代码,只是为了演示原理,没有注重其他问题,所以大家莫怪啊~

注意:数组中1代表起点,2代表终点,0代表可以通过,3代表障碍物

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #define STARTNODE   1
    4. #define ENDNODE     2
    5. #define BARRIER     3
    6. typedef struct AStarNode
    7. {
    8. int s_x;    // 坐标(最终输出路径需要)
    9. int s_y;
    10. int s_g;    // 起点到此点的距离( 由g和h可以得到f,此处f省略,f=g+h )
    11. int s_h;    // 启发函数预测的此点到终点的距离
    12. int s_style;// 结点类型:起始点,终点,障碍物
    13. struct AStarNode * s_parent;    // 父节点
    14. int s_is_in_closetable;     // 是否在close表中
    15. int s_is_in_opentable;      // 是否在open表中
    16. }AStarNode, *pAStarNode;
    17. AStarNode  map_maze[10][10];        // 结点数组
    18. pAStarNode open_table[100];     // open表
    19. pAStarNode close_table[100];        // close表
    20. int<span style="white-space:pre">   </span>   open_node_count;  <span style="white-space:pre">  </span>// open表中节点数量
    21. int    close_node_count;    <span style="white-space:pre">  </span>// close表中结点数量
    22. pAStarNode path_stack[100];     // 保存路径的栈
    23. int        top = -1;            // 栈顶
    24. // 交换两个元素
    25. //
    26. void swap( int idx1, int idx2 )
    27. {
    28. pAStarNode tmp = open_table[idx1];
    29. open_table[idx1] = open_table[idx2];
    30. open_table[idx2] = tmp;
    31. }
    32. // 堆调整
    33. //
    34. void adjust_heap( int /*i*/nIndex )
    35. {
    36. int curr = nIndex;
    37. int child = curr * 2 + 1;   // 得到左孩子idx( 下标从0开始,所有做孩子是curr*2+1 )
    38. int parent = ( curr - 1 ) / 2;  // 得到双亲idx
    39. if (nIndex < 0 || nIndex >= open_node_count)
    40. {
    41. return;
    42. }
    43. // 往下调整( 要比较左右孩子和cuur parent )
    44. //
    45. while ( child < open_node_count )
    46. {
    47. // 小根堆是双亲值小于孩子值
    48. //
    49. if ( child + 1 < open_node_count && open_table[child]->s_g + open_table[child]->s_h  > open_table[child+1]->s_g + open_table[child+1]->s_h )
    50. {
    51. ++child;<span style="white-space:pre">              </span>// 判断左右孩子大小
    52. }
    53. if (open_table[curr]->s_g + open_table[curr]->s_h <= open_table[child]->s_g + open_table[child]->s_h)
    54. {
    55. break;
    56. }
    57. else
    58. {
    59. swap( child, curr );            // 交换节点
    60. curr = child;               // 再判断当前孩子节点
    61. child = curr * 2 + 1;           // 再判断左孩子
    62. }
    63. }
    64. if (curr != nIndex)
    65. {
    66. return;
    67. }
    68. // 往上调整( 只需要比较cuur child和parent )
    69. //
    70. while (curr != 0)
    71. {
    72. if (open_table[curr]->s_g + open_table[curr]->s_h >= open_table[parent]->s_g + open_table[parent]->s_h)
    73. {
    74. break;
    75. }
    76. else
    77. {
    78. swap( curr, parent );
    79. curr = parent;
    80. parent = (curr-1)/2;
    81. }
    82. }
    83. }
    84. // 判断邻居点是否可以进入open表
    85. //
    86. void insert_to_opentable( int x, int y, pAStarNode curr_node, pAStarNode end_node, int w )
    87. {
    88. int i;
    89. if ( map_maze[x][y].s_style != BARRIER )        // 不是障碍物
    90. {
    91. if ( !map_maze[x][y].s_is_in_closetable )   // 不在闭表中
    92. {
    93. if ( map_maze[x][y].s_is_in_opentable ) // 在open表中
    94. {
    95. // 需要判断是否是一条更优化的路径
    96. //
    97. if ( map_maze[x][y].s_g > curr_node->s_g + w )    // 如果更优化
    98. {
    99. map_maze[x][y].s_g = curr_node->s_g + w;
    100. map_maze[x][y].s_parent = curr_node;
    101. for ( i = 0; i < open_node_count; ++i )
    102. {
    103. if ( open_table[i]->s_x == map_maze[x][y].s_x && open_table[i]->s_y == map_maze[x][y].s_y )
    104. {
    105. break;
    106. }
    107. }
    108. adjust_heap( i );                   // 下面调整点
    109. }
    110. }
    111. else                                    // 不在open中
    112. {
    113. map_maze[x][y].s_g = curr_node->s_g + w;
    114. map_maze[x][y].s_h = abs(end_node->s_x - x ) + abs(end_node->s_y - y);
    115. map_maze[x][y].s_parent = curr_node;
    116. map_maze[x][y].s_is_in_opentable = 1;
    117. open_table[open_node_count++] = &(map_maze[x][y]);
    118. }
    119. }
    120. }
    121. }
    122. // 查找邻居
    123. // 对上下左右8个邻居进行查找
    124. //
    125. void get_neighbors( pAStarNode curr_node, pAStarNode end_node )
    126. {
    127. int x = curr_node->s_x;
    128. int y = curr_node->s_y;
    129. // 下面对于8个邻居进行处理!
    130. //
    131. if ( ( x + 1 ) >= 0 && ( x + 1 ) < 10 && y >= 0 && y < 10 )
    132. {
    133. insert_to_opentable( x+1, y, curr_node, end_node, 10 );
    134. }
    135. if ( ( x - 1 ) >= 0 && ( x - 1 ) < 10 && y >= 0 && y < 10 )
    136. {
    137. insert_to_opentable( x-1, y, curr_node, end_node, 10 );
    138. }
    139. if ( x >= 0 && x < 10 && ( y + 1 ) >= 0 && ( y + 1 ) < 10 )
    140. {
    141. insert_to_opentable( x, y+1, curr_node, end_node, 10 );
    142. }
    143. if ( x >= 0 && x < 10 && ( y - 1 ) >= 0 && ( y - 1 ) < 10 )
    144. {
    145. insert_to_opentable( x, y-1, curr_node, end_node, 10 );
    146. }
    147. if ( ( x + 1 ) >= 0 && ( x + 1 ) < 10 && ( y + 1 ) >= 0 && ( y + 1 ) < 10 )
    148. {
    149. insert_to_opentable( x+1, y+1, curr_node, end_node, 14 );
    150. }
    151. if ( ( x + 1 ) >= 0 && ( x + 1 ) < 10 && ( y - 1 ) >= 0 && ( y - 1 ) < 10 )
    152. {
    153. insert_to_opentable( x+1, y-1, curr_node, end_node, 14 );
    154. }
    155. if ( ( x - 1 ) >= 0 && ( x - 1 ) < 10 && ( y + 1 ) >= 0 && ( y + 1 ) < 10 )
    156. {
    157. insert_to_opentable( x-1, y+1, curr_node, end_node, 14 );
    158. }
    159. if ( ( x - 1 ) >= 0 && ( x - 1 ) < 10 && ( y - 1 ) >= 0 && ( y - 1 ) < 10 )
    160. {
    161. insert_to_opentable( x-1, y-1, curr_node, end_node, 14 );
    162. }
    163. }
    164. int main()
    165. {
    166. // 地图数组的定义
    167. //
    168. AStarNode *start_node;          // 起始点
    169. AStarNode *end_node;            // 结束点
    170. AStarNode *curr_node;           // 当前点
    171. int       is_found;         // 是否找到路径
    172. int maze[][10] ={           // 仅仅为了好赋值给map_maze
    173. { 1,0,0,3,0,3,0,0,0,0 },
    174. { 0,0,3,0,0,3,0,3,0,3 },
    175. { 3,0,0,0,0,3,3,3,0,3 },
    176. { 3,0,3,0,0,0,0,0,0,3 },
    177. { 3,0,0,0,0,3,0,0,0,3 },
    178. { 3,0,0,3,0,0,0,3,0,3 },
    179. { 3,0,0,0,0,3,3,0,0,0 },
    180. { 0,0,2,0,0,0,0,0,0,0 },
    181. { 3,3,3,0,0,3,0,3,0,3 },
    182. { 3,0,0,0,0,3,3,3,0,3 },
    183. };
    184. int       i,j,x;
    185. // 下面准备点
    186. //
    187. for( i = 0; i < 10; ++i )
    188. {
    189. for ( j = 0; j < 10; ++j )
    190. {
    191. map_maze[i][j].s_g = 0;
    192. map_maze[i][j].s_h = 0;
    193. map_maze[i][j].s_is_in_closetable = 0;
    194. map_maze[i][j].s_is_in_opentable = 0;
    195. map_maze[i][j].s_style = maze[i][j];
    196. map_maze[i][j].s_x = i;
    197. map_maze[i][j].s_y = j;
    198. map_maze[i][j].s_parent = NULL;
    199. if ( map_maze[i][j].s_style == STARTNODE )  // 起点
    200. {
    201. start_node = &(map_maze[i][j]);
    202. }
    203. else if( map_maze[i][j].s_style == ENDNODE )    // 终点
    204. {
    205. end_node = &(map_maze[i][j]);
    206. }
    207. printf("%d ", maze[i][j]);
    208. }
    209. printf("\n");
    210. }
    211. // 下面使用A*算法得到路径
    212. //
    213. open_table[open_node_count++] = start_node;         // 起始点加入open表
    214. start_node->s_is_in_opentable = 1;               // 加入open表
    215. start_node->s_g = 0;
    216. start_node->s_h = abs(end_node->s_x - start_node->s_x) + abs(end_node->s_y - start_node->s_y);
    217. start_node->s_parent = NULL;
    218. if ( start_node->s_x == end_node->s_x && start_node->s_y == end_node->s_y )
    219. {
    220. printf("起点==终点!\n");
    221. return 0;
    222. }
    223. is_found = 0;
    224. while( 1 )
    225. {
    226. // for test
    227. //
    228. /*      for ( x = 0; x < open_node_count; ++x )
    229. {
    230. printf("(%d,%d):%d   ", open_table[x]->s_x, open_table[x]->s_y, open_table[x]->s_g+open_table[x]->s_h);
    231. }
    232. printf("\n\n");
    233. */
    234. curr_node = open_table[0];      // open表的第一个点一定是f值最小的点(通过堆排序得到的)
    235. open_table[0] = open_table[--open_node_count];  // 最后一个点放到第一个点,然后进行堆调整
    236. adjust_heap( 0 );               // 调整堆
    237. close_table[close_node_count++] = curr_node;    // 当前点加入close表
    238. curr_node->s_is_in_closetable = 1;       // 已经在close表中了
    239. if ( curr_node->s_x == end_node->s_x && curr_node->s_y == end_node->s_y )// 终点在close中,结束
    240. {
    241. is_found = 1;
    242. break;
    243. }
    244. get_neighbors( curr_node, end_node );           // 对邻居的处理
    245. if ( open_node_count == 0 )             // 没有路径到达
    246. {
    247. is_found = 0;
    248. break;
    249. }
    250. }
    251. if ( is_found )
    252. {
    253. curr_node = end_node;
    254. while( curr_node )
    255. {
    256. path_stack[++top] = curr_node;
    257. curr_node = curr_node->s_parent;
    258. }
    259. while( top >= 0 )        // 下面是输出路径看看~
    260. {
    261. if ( top > 0 )
    262. {
    263. printf("(%d,%d)-->", path_stack[top]->s_x, path_stack[top--]->s_y);
    264. }
    265. else
    266. {
    267. printf("(%d,%d)", path_stack[top]->s_x, path_stack[top--]->s_y);
    268. }
    269. }
    270. }
    271. else
    272. {
    273. printf("么有找到路径");
    274. }
    275. puts("");
    276. return 0;
    277. }

【转】A* A星 算法 C语言 实现代码的更多相关文章

  1. POJ 2449 Remmarguts' Date (SPFA + A星算法) - from lanshui_Yang

    题目大意:给你一个有向图,并给你三个数s.t 和 k ,让你求从点 s 到 点 t 的第 k 短的路径.如果第 k 短路不存在,则输出“-1” ,否则,输出第 k 短路的长度. 解题思路:这道题是一道 ...

  2. 算法起步之A星算法

    原文:算法起步之A星算法 用途: 寻找最短路径,优于bfs跟dfs 描述: 基本描述是,在深度优先搜索的基础上,增加了一个启发式算法,在选择节点的过程中,不是盲目选择,而是有目的的选的,F=G+H,f ...

  3. Cocos2d-x 3.1.1 学习日志16--A星算法(A*搜索算法)学问

    A *搜索算法称为A星算法.这是一个在图形平面,路径.求出最低通过成本的算法. 经常使用于游戏中的NPC的移动计算,或线上游戏的BOT的移动计算上. 首先:1.在Map地图中任取2个点,開始点和结束点 ...

  4. A*搜寻算法(A星算法)

    A*搜寻算法[编辑] 维基百科,自由的百科全书 本条目需要补充更多来源.(2015年6月30日) 请协助添加多方面可靠来源以改善这篇条目,无法查证的内容可能会被提出异议而移除. A*搜索算法,俗称A星 ...

  5. 【转】TEA、XTEA、XXTEA加密解密算法(C语言实现)

    ref : https://blog.csdn.net/gsls200808/article/details/48243019 在密码学中,微型加密算法(Tiny Encryption Algorit ...

  6. 【转】位置式、增量式PID算法C语言实现

    位置式.增量式PID算法C语言实现 芯片:STM32F107VC 编译器:KEIL4 作者:SY 日期:2017-9-21 15:29:19 概述 PID 算法是一种工控领域常见的控制算法,用于闭环反 ...

  7. PID算法(C语言)

    /************ PID算法(C语言) ************/ #include <stdio.h> #include<math.h> struct _pid { ...

  8. Java开源-astar:A 星算法

    astar A星算法Java实现 一.适用场景 在一张地图中,绘制从起点移动到终点的最优路径,地图中会有障碍物,必须绕开障碍物. 二.算法思路 1. 回溯法得到路径 (如果有路径)采用“结点与结点的父 ...

  9. A星算法(Java实现)

    一.适用场景 在一张地图中.绘制从起点移动到终点的最优路径,地图中会有障碍物.必须绕开障碍物. 二.算法思路 1. 回溯法得到路径 (假设有路径)採用"结点与结点的父节点"的关系从 ...

随机推荐

  1. Accelerated C++:通过演示样例进行编程实践——练习解答(第9章)

    我的Github地址:https://github.com/lanbeilyj/Accerlerated-C-plus-plus 9-0. Compile, execute, and test the ...

  2. springMVC视图解析器——InternalResourceViewResolver(转)

    springmvc在处理器方法中通常返回的是逻辑视图,如何定位到真正的页面,就需要通过视图解析器. springmvc里提供了多个视图解析器,InternalResourceViewResolver就 ...

  3. JS里面的indexOf()函数

    stringObject.indexOf(searchvalue,formindex); searchvalue在字符串首次出现的位置,位置是从0开始算的.

  4. multi_input_paths

  5. Spring+Struts2+Hibernate的整合

    这篇主要采用Maven搭建Spring+Struts2+Hibernate的整合项目,复习一下SSH框架,虽然spring提供自己的MVC框架, 但是Spring也提供和其他框架的无缝整合,采用组件形 ...

  6. linux cmd cp -a

    cp -a 在保留原文件属性的前提下复制文件    cp -r dirname destdir   复制目录后其文件属性会发生变化 想要使得复制之后的目录和原目录完全一样,可以使用cp -a dirn ...

  7. Maven报错Missing artifact jdk.tools:jdk.tools:jar:1.7--转

    原文地址:http://blog.csdn.net/u013281331/article/details/40824707 在Eclipse中检出Maven工程,一直报这个错:“Missing art ...

  8. POJ 1595 Prime Cuts (ZOJ 1312) 素数打表

    ZOJ:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=312 POJ:http://poj.org/problem?id=159 ...

  9. 关于IO重定向

    首先,Unix进程使用文件描述符0,1,2作为标准输入.输出和错误的通道. 其次,当进程请求一个新的文件描述符的时候,系统内核将最低可用的文件描述符赋给它. 第三,文件描述符集合通过exec调用传递, ...

  10. JSP中文件的上传于下载演示样例

    一.文件上传的原理     1.文件上传的前提:         a.form表单的method必须是post         b.form表单的enctype必须是multipart/form-da ...