zoukankan      html  css  js  c++  java
  • A*算法(附c源码)

    关于A*算法网上介绍的有很多,我只是看了之后对这个算法用c写了一下,并测试无误后上传以分享一下,欢迎指正!下面是我找的一个介绍,并主要根据这个实现的。

            寻路算法不止 A* 这一种, 还有递归, 非递归, 广度优先, 深度优先, 使用堆栈等等, 有兴趣的可以研究研究~~

    简易地图

            如图所示简易地图, 其中绿色方块的是起点 (用 A 表示), 中间蓝色的是障碍物, 红色的方块 (用 B 表示) 是目的地. 为了可以用一个二维数组来表示地图, 我们将地图划分成一个个的小方块.

            二维数组在游戏中的应用是很多的, 比如贪吃蛇和俄罗斯方块基本原理就是移动方块而已. 而大型游戏的地图, 则是将各种"地貌"铺在这样的小方块上.

    寻路步骤

            1. 从起点A开始, 把它作为待处理的方格存入一个"开启列表", 开启列表就是一个等待检查方格的列表.

            2. 寻找起点A周围可以到达的方格, 将它们放入"开启列表", 并设置它们的"父方格"为A.

            3. 从"开启列表"中删除起点 A, 并将起点 A 加入"关闭列表", "关闭列表"中存放的都是不需要再次检查的方格

            图中浅绿色描边的方块表示已经加入 "开启列表" 等待检查. 淡蓝色描边的起点 A 表示已经放入 "关闭列表" , 它不需要再执行检查.

            从 "开启列表" 中找出相对最靠谱的方块, 什么是最靠谱? 它们通过公式 F=G+H 来计算.

            F = G + H

                    G 表示从起点 A 移动到网格上指定方格的移动耗费 (可沿斜方向移动).

                    H 表示从指定的方格移动到终点 B 的预计耗费 (H 有很多计算方法, 这里我们设定只可以上下左右移动).

            我们假设横向移动一个格子的耗费为10, 为了便于计算, 沿斜方向移动一个格子耗费是14. 为了更直观的展示如何运算 FGH, 图中方块的左上角数字表示 F, 左下角表示 G, 右下角表示 H. 看看是否跟你心里想的结果一样?

            从 "开启列表" 中选择 F 值最低的方格 C (绿色起始方块 A 右边的方块), 然后对它进行如下处理:

            4. 把它从 "开启列表" 中删除, 并放到 "关闭列表" 中.

            5. 检查它所有相邻并且可以到达 (障碍物和 "关闭列表" 的方格都不考虑) 的方格. 如果这些方格还不在 "开启列表" 里的话, 将它们加入 "开启列表", 计算这些方格的 G, H 和 F 值各是多少, 并设置它们的 "父方格" 为 C.

            6. 如果某个相邻方格 D 已经在 "开启列表" 里了, 检查如果用新的路径 (就是经过C 的路径) 到达它的话, G值是否会更低一些, 如果新的G值更低, 那就把它的 "父方格" 改为目前选中的方格 C, 然后重新计算它的 F 值和 G 值 (H 值不需要重新计算, 因为对于每个方块, H 值是不变的). 如果新的 G 值比较高, 就说明经过 C 再到达 D 不是一个明智的选择, 因为它需要更远的路, 这时我们什么也不做.

            如图, 我们选中了 C 因为它的 F 值最小, 我们把它从 "开启列表" 中删除, 并把它加入 "关闭列表". 它右边上下三个都是墙, 所以不考虑它们. 它左边是起始方块, 已经加入到 "关闭列表" 了, 也不考虑. 所以它周围的候选方块就只剩下 4 个. 让我们来看看 C 下面的那个格子, 它目前的 G 是14, 如果通过 C 到达它的话, G将会是 10 + 10, 这比 14 要大, 因此我们什么也不做.

            然后我们继续从 "开启列表" 中找出 F 值最小的, 但我们发现 C 上面的和下面的同时为 54, 这时怎么办呢? 这时随便取哪一个都行, 比如我们选择了 C 下面的那个方块 D.

            D 右边已经右上方的都是墙, 所以不考虑, 但为什么右下角的没有被加进 "开启列表" 呢? 因为如果 C 下面的那块也不可以走, 想要到达 C 右下角的方块就需要从 "方块的角" 走了, 在程序中设置是否允许这样走. (图中的示例不允许这样走)

            就这样, 我们从 "开启列表" 找出 F 值最小的, 将它从 "开启列表" 中移掉, 添加到 "关闭列表". 再继续找出它周围可以到达的方块, 如此循环下去...

            那么什么时候停止呢? —— 当我们发现 "开始列表" 里出现了目标终点方块的时候, 说明路径已经被找到.

    如何找回路径

            如上图所示, 除了起始方块, 每一个曾经或者现在还在 "开启列表" 里的方块, 它都有一个 "父方块", 通过 "父方块" 可以索引到最初的 "起始方块", 这就是路径.

    以上转自http://www.cnblogs.com/technology/archive/2011/05/26/2058842.html

    下面是我的代码(c):

    一共三个文件:Apath.h 、Apath.c 、main.c 代码中有详细注释。

     1  #include <stdio.h>
     2  #include <stdlib.h>
     3  #include <string.h>
     4  #include <stddef.h>
     5  #include <stdbool.h>
     6 
     7  #ifndef APATH_H
     8  #define APATH_H
     9  #endif
    10 
    11  #define TURE 1
    12  #define FAULT 0
    13 
    14  //约定:0是可走的,1表示障碍物不可走,2表示起点,3表示终点,4表示路径
    15  #define int_0 0
    16  #define int_1 1
    17  #define int_2 2
    18  #define int_3 3
    19  #define int_4 4
    20 
    21  #define MAP_MAX_X 10 //地图边界,二维数组大小
    22  #define MAP_MAX_Y 10
    23 
    24 typedef struct LNode {
    25     int data;                   //对应数组中的数值
    26     int F;                      //F = G + H;
    27     int G;                      //G:从起点 A 移动到指定方格的移动代价,沿着到达该方格而生成的路径
    28     int H;                      //H:从指定的方格移动到终点 B 的估算成本
    29     int x, y;                   //对应数组中的坐标
    30     bool OPen_flag;             //在开放列表中为1,不在为0
    31     bool Close_flag;            //在关闭列表中为1,不在为0
    32     struct LNode* next;         //用于链表排序
    33     struct LNode* path_next;    //用于最终找到的路径
    34 }LNode, *LinkList;
    35 
    36  LinkList InitList(); //返回一个初始化的链表
    37  LNode** malloc_array2D(int row, int col);
    38  void free_array2D(LNode **arr);
    39  LNode** Translate_array(int array[][10], int row, int col); //将一个普通数组翻译为单链表节点的数组
    40  void output(LNode **array, int row, int col);
    41 
    42  LNode* find_start_LNode(LNode** Arr, int row, int col); //从数组中找到始点
    43  LNode* find_end_LNode(LNode** Arr, int row, int col); //从数组中找到终点
    44 
    45  //忘记这些要干嘛了,重写吧
    46  //bool isExist_ALNode_in_List(LNode* curLNode, LinkList L_OpenList); //查看节点是否在链表中,在返回ture,不在返回fault
    47  //对关闭列表中的当前节点进行检查,看它周围的节点是否在OpenList链表里,不在:添加进去;在:检查经过它到达起点的G是否最小,是:修改,不是:不修改
    48  //LNode* check_CloseList_curLNode(LNode* curLNode, LNode* endLNode, LinkList L_OpenList, LinkList L_CloseList, LNode** Arr);
    49 
    50  LNode* pop_OpenList_minNode(LinkList L_OpenList); //返回开放列表中F值最小的节点
    51  void push_OpenList_Node(LinkList L, LNode *elem); //插入一个节点并排序
    52  bool insert_Into_CloseList(LNode* min_Open, LinkList L_CloseList);//插入OpenList中F值最小的节点到CloseList中去
    53 
    54 
    55  int count_LNode_G(LNode* curLNode, LNode* aheadLNode); //计算节点的G值
    56  int count_LNode_H(LNode* curLNode, LNode* endLNode); //计算节点的H值
    57  int count_LNode_F(LNode* curLNode); //计算节点的F值
    58 
    59  bool isExist_openList(LNode* curLNode); //查看节点是否在链表中,在返回ture,不在返回fault
    60  bool isExist_closeList(LNode* curLNode);
    61  bool isobstacle(LNode* curLNode);
    62  void check_around_curNode(LNode* cur, LNode* endLNode, LinkList open_list, LNode** Arr); //检查周围的节点,是否合适加入开放列表
    Apath.h
      1 #include "Apath.h"
      2 
      3 LinkList InitList()
      4 {
      5     LinkList L = (LinkList)malloc(sizeof(LNode));
      6     if (L == NULL)
      7     {
      8         printf("Defeat!");
      9         exit(1);
     10     }
     11     memset(L, 0, sizeof(LNode));
     12 
     13     return L;
     14 }//LinkList()
     15 
     16 LNode** malloc_array2D(int row, int col)
     17 {
     18     LNode** map = (LNode**)malloc(row * sizeof(LNode*) + row * col * sizeof(LNode));
     19     LNode* head = (LNode*)(map + row);
     20     for (int i = 0; i < row; ++i)
     21         map[i] = head + i * col;
     22 
     23     return map;
     24 }
     25 
     26 LNode** Translate_array(int array[][10], int row, int col)
     27 {
     28     LNode **map = malloc_array2D(10, 10);
     29     for (int i = 0; i < row; ++i)
     30         for (int j = 0; j < col; ++j)
     31         {
     32             (map[i] + j)->data = array[i][j];
     33             (map[i] + j)->G = 0;
     34             (map[i] + j)->H = 0;
     35             (map[i] + j)->F = 0; //(map[i] + j)->G + (map[i] + j)->H;
     36             (map[i] + j)->x = i;
     37             (map[i] + j)->y = j;
     38             (map[i] + j)->Close_flag = 0;
     39             (map[i] + j)->OPen_flag = 0;
     40             (map[i] + j)->next = NULL;
     41             (map[i] + j)->path_next = NULL;
     42         }
     43     return map;
     44 }
     45 
     46 void free_array2D(LNode **arr)
     47 {
     48     free(arr);
     49 }
     50 
     51 //二维数组的访问必须指明位数,否则编译器不能解析
     52 void output(LNode** array, int row, int col)
     53 {
     54     //for (int i = 0; i < row; ++i)
     55     // for (int j = 0; j < col; ++j)
     56     // {
     57     // (array[i] + j)->F = j;
     58     // }
     59     for (int i = 0; i < row; ++i)
     60     {
     61         for (int j = 0; j < col; ++j)
     62         {
     63             printf("%d	", (array[i] + j)->data);
     64         }
     65         printf("
    ");
     66     }
     67 
     68     for (int i = 0; i < row; ++i)
     69     {
     70         for (int j = 0; j < col; ++j)
     71         {
     72             printf("(%d,%d),%d	", i, j, (array[i] + j)->data);
     73         }
     74         printf("
    ");
     75     }
     76 }
     77 
     78 //从数组中找到始点
     79 LNode* find_start_LNode(LNode** Arr, int row, int col)
     80 {
     81     LNode* start_LNode = NULL;
     82     for (int i = 0; i < row; ++i)
     83     {
     84         for (int j = 0; j < col; ++j)
     85         {
     86             if (2 == (Arr[i] + j)->data)
     87             {
     88                 start_LNode = (Arr[i] + j);
     89                 //起点H=0,G=0,F=0
     90                 start_LNode->G = 0;
     91                 start_LNode->H = 0;
     92                 start_LNode->F = 0; //起点,则默认所有值为0
     93                 return start_LNode; //返回节点
     94             }
     95         }
     96     }
     97     return NULL;
     98 }
     99 
    100 //从数组中找到终点
    101 LNode* find_end_LNode(LNode** Arr, int row, int col)
    102 {
    103     LNode* end_LNode = NULL;
    104     for (int i = 0; i < row; ++i)
    105     {
    106         for (int j = 0; j < col; ++j)
    107         {
    108             if (3 == (Arr[i] + j)->data)
    109             {
    110                 end_LNode = (*(Arr + i) + j);
    111                 end_LNode->F = 0;
    112                 end_LNode->G = 0;
    113                 end_LNode->H = 0;
    114                 return end_LNode; //返回节点
    115             }
    116         }
    117     }
    118     return NULL;
    119 }
    120 
    121 //计算节点的G值
    122 int count_LNode_G(LNode* curLNode, LNode* aheadLNode)
    123 {
    124     if (curLNode->x == aheadLNode->x && curLNode->y == aheadLNode->y)
    125         return 0;
    126 
    127     if (aheadLNode->x - curLNode->x != 0 && aheadLNode->y - curLNode->y != 0)
    128         curLNode->G = aheadLNode->G + 14;
    129     else
    130         curLNode->G = aheadLNode->G + 10;
    131     return curLNode->G;
    132 }
    133 
    134 //计算节点的H值
    135 int count_LNode_H(LNode* curLNode, LNode* endLNode)
    136 {
    137     curLNode->H = abs(endLNode->x - curLNode->x) * 10 + abs(endLNode->y - curLNode->y) * 10;
    138     return curLNode->H;
    139 }
    140 
    141 //计算节点的F值
    142 int count_LNode_F(LNode* curLNode)
    143 {
    144     curLNode->F = curLNode->G + curLNode->H;
    145     return curLNode->F;
    146 }
    147 
    148 //按从小到大的顺序
    149 void push_OpenList_Node(LinkList L, LNode *elem)
    150 {
    151     LNode *p, *q;
    152     p = q = L;
    153     while (p->next != NULL && p->F < elem->F)
    154     {
    155         q = p;
    156         p = p->next;
    157     }
    158 
    159     if (p->F < elem->F)
    160         q = p;
    161 
    162     elem->next = q->next;
    163     q->next = elem;
    164     //插入成功,更改属性值OPen_flag = 1
    165     elem->OPen_flag = 1;
    166 }
    167 
    168 //返回开放列表中F值最小的节点
    169 LNode* pop_OpenList_minNode(LinkList L_OpenList)
    170 {
    171     LNode *elem = NULL;
    172     if (L_OpenList->next) //为了安全,防止访问空指针
    173     {
    174         L_OpenList->next->OPen_flag = 0;
    175         elem = L_OpenList->next;
    176         L_OpenList->next = L_OpenList->next->next;
    177         elem->next = NULL;
    178     }
    179     else
    180         printf("have a NULL point in pop_OpenList_mimNode()");
    181     return elem;
    182 }
    183 
    184 //插入OpenList中F值最小的节点到CloseList中去
    185 bool insert_Into_CloseList(LNode* min_Open, LinkList L_CloseList)
    186 {
    187     //对于CloseList中的节点并不需要排序,采用头插法
    188     min_Open->next = L_CloseList->next;
    189     L_CloseList->next = min_Open;
    190     min_Open->Close_flag = 1;
    191     return TURE;
    192 }
    193 
    194 
    195 bool isExist_openList(LNode* curLNode)
    196 {
    197     return curLNode->OPen_flag;
    198 }
    199 
    200 bool isExist_closeList(LNode* curLNode)
    201 {
    202     return curLNode->Close_flag;
    203 }
    204 
    205 bool isobstacle(LNode* curLNode)
    206 {
    207     if (curLNode->data == 1)
    208         return TURE;
    209     else
    210         return FAULT;
    211 }
    212 
    213 //该节点是否可以加入开放列表
    214 bool CanJoinOpenList(LNode* cur)
    215 {
    216     if (cur->x > -1 && cur->y > -1) //边界检测
    217     {
    218         if (!isExist_closeList(cur) && !isobstacle(cur)) //既不在关闭列表里,也不是障碍物
    219         {
    220             return TURE;
    221         }
    222         else
    223             return FAULT;
    224     }
    225     return FAULT;
    226 }
    227 
    228 void insert_open(LNode *Node, LNode* ahead, LNode* endLNode, LinkList open_list, LNode** Arr)
    229 {
    230     if (!CanJoinOpenList(Node))
    231         return;
    232 
    233     if (isExist_openList(Node))
    234     {
    235         //经由ahead节点,会不会使得F的值更小
    236         if (Node->x - ahead->x != 0 && Node->y - ahead->y != 0)
    237         {
    238             if (Node->F > (ahead->F + 14))
    239             {
    240                 count_LNode_G(Node, ahead);
    241                 count_LNode_F(Node); //H值没有改变,所以还是原来的值
    242                 Node->path_next = ahead; //也不用再插入
    243             }
    244         }
    245         else
    246         {
    247             if (Node->F > (ahead->F + 10))
    248             {
    249                 count_LNode_G(Node, ahead);
    250                 count_LNode_F(Node); //H值没有改变,所以还是原来的值
    251                 Node->path_next = ahead; //也不用再插入
    252             }
    253         }
    254     }
    255     else 
    256     {
    257         count_LNode_G(Node, ahead);
    258         count_LNode_H(Node, endLNode);
    259         count_LNode_F(Node);
    260         Node->path_next = ahead;
    261         push_OpenList_Node(open_list, Node);
    262     }
    263 }
    264 
    265 void check_around_curNode(LNode* cur, LNode* endLNode, LinkList open_list, LNode** Arr)
    266 {
    267     const int x = cur->x;
    268     const int y = cur->y;
    269 
    270     //检查边界,画图标记下已经检查的节点
    271     if (y + 1 < MAP_MAX_Y)
    272     {
    273         insert_open(Arr[x] + y + 1, cur, endLNode, open_list, Arr);
    274 
    275         if (x + 1 < MAP_MAX_X)
    276             insert_open(Arr[x + 1] + y + 1, cur, endLNode, open_list, Arr);
    277 
    278         if(x - 1 >= 0)
    279             insert_open(Arr[x - 1] + y + 1, cur, endLNode, open_list, Arr);
    280     }
    281 
    282     if (x + 1 < MAP_MAX_X)
    283     {
    284         insert_open(Arr[x + 1] + y, cur, endLNode, open_list, Arr);
    285 
    286         if(y - 1 >= 0)
    287             insert_open(Arr[x + 1] + y - 1, cur, endLNode, open_list, Arr);
    288     }
    289 
    290     if (y - 1 >= 0)
    291     {
    292         insert_open(Arr[x] + y - 1, cur, endLNode, open_list, Arr);
    293 
    294         if (x - 1 >= 0)
    295             insert_open(Arr[x - 1] + y - 1, cur, endLNode, open_list, Arr);
    296     }
    297 
    298     if (x - 1 >= 0)
    299         insert_open(Arr[x - 1] + y, cur, endLNode, open_list, Arr);
    300 
    301     //insert_open(Arr[x] + y - 1, cur, endLNode, open_list, Arr);
    302     //insert_open(Arr[x] + y + 1, cur, endLNode, open_list, Arr);
    303     //insert_open(Arr[x + 1] + y, cur, endLNode, open_list, Arr);
    304     //insert_open(Arr[x + 1] + y - 1, cur, endLNode, open_list, Arr);
    305     //insert_open(Arr[x + 1] + y + 1, cur, endLNode, open_list, Arr);
    306     //insert_open(Arr[x - 1] + y, cur, endLNode, open_list, Arr);
    307     //insert_open(Arr[x - 1] + y + 1, cur, endLNode, open_list, Arr);
    308     //insert_open(Arr[x - 1] + y - 1, cur, endLNode, open_list, Arr);
    309 }
    Apath.c
     1  #include <stdio.h>
     2  #include "Apath.h"
     3 
     4  //为简单,干脆把把下面数组转为链表结构的数组
     5  //约定:0是可走的,1表示障碍物不可走,2表示起点,3表示终点,4表示路径
     6 int array[10][10] = {
     7 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
     8 { 0, 0, 0, 1, 1, 0, 0, 0, 0, 0 },
     9 { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
    10 { 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 },
    11 { 0, 0, 0, 0, 0, 1, 3, 0, 0, 0 },
    12 { 0, 0, 2, 0, 0, 1, 0, 0, 0, 0 },
    13 { 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 },
    14 { 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 },
    15 { 0, 0, 0, 1, 1, 0, 0, 0, 0, 0 },
    16 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } };
    17 
    18  int main()
    19  {
    20      int row = MAP_MAX_X, col = MAP_MAX_Y;
    21      printf("hello world!
    ");
    22      LNode **map = Translate_array(array, row, col); //这里将数组的地图转为节点map的地图
    23      output(map, 10, 10);
    24 
    25      LinkList open_List = InitList(); //定义并初始化一个开放列表
    26      LinkList close_List = InitList(); //一个封闭列表
    27      LNode* startLNode = find_start_LNode(map, row, col);
    28      LNode* endLNode = find_end_LNode(map, row, col);
    29 
    30      LNode* curLNode = startLNode; //当前节点=开始节点
    31      curLNode->G = 0; //计算节点的三个值
    32 
    33      count_LNode_H(curLNode, endLNode);
    34      count_LNode_F(curLNode);
    35      push_OpenList_Node(open_List, curLNode); //先将开始节点插入开放列表
    36 
    37      while (curLNode->data != 3)
    38      {
    39          //LNode *e = NULL;
    40          curLNode = pop_OpenList_minNode(open_List);
    41          insert_Into_CloseList(curLNode, close_List);
    42          //2、查看起点周围的点是否在开放列表里,不在加入,在检测经过该点F值是否最小等;
    43          check_around_curNode(curLNode, endLNode, open_List, map);
    44      }
    45 
    46      while (endLNode->path_next)
    47      {
    48          printf("x:%d---y:%d
    ", endLNode->path_next->x, endLNode->path_next->y);
    49          endLNode->path_next = endLNode->path_next->path_next;
    50      }
    51 
    52      free_array2D(map);
    53      return 0;
    54  }
    main.c

    测试结果(红线就是要找的路线):

  • 相关阅读:
    poj 2763 Housewife Wind
    hdu 3966 Aragorn's Story
    poj 1655 Balancing Act 求树的重心
    有上下界的网络流问题
    URAL 1277 Cops and Thieves 最小割 无向图点带权点连通度
    ZOJ 2532 Internship 网络流求关键边
    ZOJ 2760 How Many Shortest Path 最大流+floyd求最短路
    SGU 438 The Glorious Karlutka River =) 拆点+动态流+最大流
    怎么样仿写已知网址的网页?
    5-10 公路村村通 (30分)
  • 原文地址:https://www.cnblogs.com/mingbujian/p/4915546.html
Copyright © 2011-2022 走看看