当前位置:   article > 正文

基于C++实现的A*算法(链表和二叉堆实现)_a*算法是不是必须用到链表?

a*算法是不是必须用到链表?

基于C++实现的A*算法

  • AStar算法相对于Dijkstra算法而言升级的地方在于引入了启发距离,即H(当前点到终点的预计距离),因此在每次大循环中Dijkstra算法找的最短距离仅仅是G(起点到当前点的距离),而AStar算法找的是(G+H)即F的最小值,因此AStar算法可以更快的找到终点,从而在判断两点最短路径时候有着更快的结束时间即更高的效率

  • 这两种算法本质上而言均是找起点到每一个点的最短距离,而AStar算法好就好在它引入了每次对终点的度量因而可以比Dijkstra算法更快的找到对终点的距离!

    其具体的原理步骤可以参见其他优秀的blog如:A* 寻路算法 - christanxw的专栏 - C++博客

基于c++的链表的A*算法代码如下:

AStar.h文件

  1. #pragma once
  2. #include<vector>
  3. #include<list>
  4. #include<memory>
  5. #include<algorithm>
  6. #include<iostream>
  7. struct ANode
  8. {
  9. ANode(int X, int Y, std::shared_ptr<ANode> p = nullptr) : x(X), y(Y), prev(p) {}
  10. int x; //点的x坐标
  11. int y; //点的y坐标
  12. int G=0; //起点到该点的欧拉距离
  13. int H=0;//该点到终点的曼哈顿距离
  14. int F=0;//G+H
  15. std::weak_ptr<ANode> prev;//指向的前一个节点!!!改成了weak_ptr不然数据多的时候析构报错
  16. };
  17. class AStar
  18. {
  19. public:
  20. AStar(std::vector<std::vector<int>> m ): maps(m) {}
  21. std::shared_ptr<ANode> findPath(std::shared_ptr<ANode> beg, std::shared_ptr<ANode> end);
  22. void PrintAStarPath(const std::pair<int, int> &, const std::pair<int, int> &);
  23. ~AStar()
  24. {
  25. openlist.clear();
  26. closeist.clear();
  27. }
  28. private:
  29. void refreshOpenList(std::shared_ptr<ANode>, std::shared_ptr<ANode> end);
  30. int calculateH(std::shared_ptr<ANode>, std::shared_ptr<ANode>) const;
  31. int calculateF(std::shared_ptr<ANode>,std::shared_ptr<ANode>) const;
  32. private:
  33. std::vector<std::vector<int>> maps;//地图
  34. std::list<std::shared_ptr<ANode>> openlist;//保存还未遍历过的节点
  35. std::list<std::shared_ptr<ANode>> closeist;//保存已经找到最短路径的节点
  36. const static int costLow;//上下位移的距离
  37. const static int costHigh;//斜向位移的距离
  38. };
  39. const int AStar::costLow = 10;
  40. const int AStar::costHigh = 14;
  41. int AStar::calculateH(std::shared_ptr<ANode> point, std::shared_ptr<ANode> end) const
  42. {
  43. return costLow * (std::abs(point->x - end->x) + std::abs(point->y - end->y));
  44. }
  45. int AStar::calculateF(std::shared_ptr<ANode> point,std::shared_ptr<ANode> end) const
  46. {
  47. return point->G + calculateH(point, end);
  48. }
  49. std::shared_ptr<ANode> AStar::findPath(std::shared_ptr<ANode> beg, std::shared_ptr<ANode> end)
  50. {
  51. refreshOpenList(beg,end);
  52. while (!openlist.empty())
  53. {
  54. auto iter = std::min_element(openlist.cbegin(), openlist.cend(), [](std::shared_ptr<ANode> p1, std::shared_ptr<ANode> p2)
  55. { return p1->F <= p2->F;});
  56. closeist.push_back(*iter);
  57. std::shared_ptr<ANode> iter_temp = *iter;
  58. openlist.erase(iter);
  59. refreshOpenList(iter_temp, end);
  60. auto iter2 = std::find_if(openlist.cbegin(), openlist.cend(), [end](std::shared_ptr<ANode> sp)
  61. { return (sp->x == end->x) && (sp->y == end->y); });
  62. if (iter2 != openlist.end())
  63. return *iter2;
  64. }
  65. return nullptr;
  66. }
  67. void AStar::refreshOpenList(std::shared_ptr<ANode> point, std::shared_ptr<ANode> end)
  68. {
  69. bool upIsWall = false;//表示当前点上有障碍物,即对应的斜向没法走
  70. bool downIsWall = false;
  71. bool leftIsWall = false;
  72. bool rightIsWall = false;
  73. if (point->x - 1 >= 0 && maps[point->x - 1][point->y] == 1)
  74. upIsWall = true;
  75. if (point->x + 1 < int(maps.size()) && maps[point->x + 1][point->y] == 1)
  76. downIsWall = true;
  77. if (point->y - 1 >= 0 && maps[point->x][point->y - 1] == 1)
  78. leftIsWall = true;
  79. if (point->y + 1 < int(maps.front().size()) && maps[point->x][point->y + 1] == 1)
  80. rightIsWall = true;
  81. //第一步初始化起点及其周围的节点
  82. if (openlist.empty() && closeist.empty())
  83. {
  84. closeist.push_back(point);
  85. for (int i = point->x - 1; i <= point->x + 1; ++i)
  86. {
  87. for (int j = point->y - 1; j <= point->y + 1; ++j)
  88. {
  89. if (i >= 0 && j >= 0 && i < int(maps.size()) && j < int(maps.front().size()) && (i != point->x || j != point->y) && !maps[i][j])
  90. {
  91. if (i != point->x && j != point->y)
  92. {
  93. if (leftIsWall && j < point->y)
  94. continue;
  95. if (rightIsWall && j > point->y)
  96. continue;
  97. if (upIsWall && i < point->x)
  98. continue;
  99. if (downIsWall && i > point->x)
  100. continue;
  101. }
  102. auto cur = std::make_shared<ANode>(i, j, point);
  103. cur->G = (i != point->x && j != point->y) ? costHigh : costLow;
  104. cur->H = calculateH(cur, end);
  105. cur->F = calculateF(cur, end);
  106. openlist.push_back(cur);
  107. }
  108. }
  109. }
  110. }
  111. //刷新每一次找到的起点到当前点的最短路径中的当前点的周围节点情况
  112. else
  113. {
  114. for (int i = point->x - 1; i <= point->x + 1; ++i)
  115. {
  116. for (int j = point->y - 1; j <= point->y + 1; ++j)
  117. {
  118. if (i >= 0 && j >= 0 && i < int(maps.size()) && j < int(maps.front().size()) && (i != point->x || j != point->y) && !maps[i][j])
  119. {
  120. if (i != point->x && j != point->y)
  121. {
  122. if (leftIsWall && j < point->y)
  123. continue;
  124. if (rightIsWall && j > point->y)
  125. continue;
  126. if (upIsWall && i < point->x)
  127. continue;
  128. if (downIsWall && i > point->x)
  129. continue;
  130. }
  131. auto cur = std::make_shared<ANode>(i, j, point);
  132. cur->G = ((i != point->x && j != point->y) ? costHigh : costLow) + point->G;
  133. cur->H = calculateH(cur, end);
  134. cur->F = calculateF(cur, end);
  135. auto iter_close=std::find_if(closeist.cbegin(), closeist.cend(), [i,j](std::shared_ptr<ANode> sp)
  136. { return (sp->x == i) && (sp->y == j); });
  137. if (iter_close == closeist.end())
  138. {
  139. auto iter_open=std::find_if(openlist.cbegin(), openlist.cend(), [i,j](std::shared_ptr<ANode> sp)
  140. { return (sp->x == i) && (sp->y == j); });
  141. if (iter_open != openlist.end())
  142. {
  143. if((*iter_open)->G > cur->G)
  144. {
  145. (*iter_open)->G = cur->G;
  146. (*iter_open)->F = (*iter_open)->G + (*iter_open)->H;
  147. (*iter_open)->prev = point;
  148. }
  149. }
  150. else
  151. openlist.push_back(cur);
  152. }
  153. }
  154. }
  155. }
  156. }
  157. }
  158. void AStar::PrintAStarPath(const std::pair<int, int>& start, const std::pair<int, int>& end)
  159. {
  160. auto start_sp = std::make_shared<ANode>(start.first, start.second), end_sp = std::make_shared<ANode>(end.first, end.second);
  161. std::shared_ptr<ANode> final = findPath(start_sp, end_sp);
  162. if (!final)
  163. std::cout << "没有找到起点到终点路径" << std::endl;
  164. else
  165. {
  166. while (final)
  167. {
  168. maps[final->x][final->y] = '*';
  169. final = final->prev.lock();
  170. }
  171. for (const auto &i : maps)
  172. {
  173. for (const auto &j : i)
  174. {
  175. if (j > 1)
  176. std::cout << char(j) << " ";
  177. else
  178. std::cout << j << " ";
  179. }
  180. std::cout << std::endl;
  181. }
  182. }
  183. }

改进后用二叉堆的AStar主文件如下 

  1. #pragma once
  2. #include<vector>
  3. #include<list>
  4. #include<memory>
  5. #include<algorithm>
  6. #include<iostream>
  7. struct ANode
  8. {
  9. ANode(int X, int Y, std::shared_ptr<ANode> p = nullptr) : x(X), y(Y), prev(p) {}
  10. int x; //点的x坐标
  11. int y; //点的y坐标
  12. int G=0; //起点到该点的欧拉距离
  13. int H=0;//该点到终点的曼哈顿距离
  14. int F=0;//G+H
  15. std::weak_ptr<ANode> prev;//指向的前一个节点
  16. };
  17. class AStar
  18. {
  19. public:
  20. AStar(std::vector<std::vector<int>> m ): maps(m) {}
  21. std::shared_ptr<ANode> findPath(std::shared_ptr<ANode> beg, std::shared_ptr<ANode> end);
  22. void PrintAStarPath(const std::pair<int, int> &, const std::pair<int, int> &);
  23. ~AStar()
  24. {
  25. openlist.clear();
  26. closeist.clear();
  27. }
  28. private:
  29. void refreshOpenList(std::shared_ptr<ANode>, std::shared_ptr<ANode> end);
  30. int calculateH(std::shared_ptr<ANode>, std::shared_ptr<ANode>) const;
  31. int calculateF(std::shared_ptr<ANode>,std::shared_ptr<ANode>) const;
  32. void HeapSort(int beg, int end);
  33. void HeapSort(int end);
  34. private:
  35. std::vector<std::vector<int>> maps;//地图
  36. std::vector<std::shared_ptr<ANode>> openlist;//保存还未遍历过的节点
  37. std::list<std::shared_ptr<ANode>> closeist;//保存已经找到最短路径的节点
  38. const static int costLow;//上下位移的距离
  39. const static int costHigh;//斜向位移的距离
  40. };
  41. const int AStar::costLow = 10;
  42. const int AStar::costHigh = 14;
  43. int AStar::calculateH(std::shared_ptr<ANode> point, std::shared_ptr<ANode> end) const
  44. {
  45. return costLow * (std::abs(point->x - end->x) + std::abs(point->y - end->y));
  46. }
  47. int AStar::calculateF(std::shared_ptr<ANode> point,std::shared_ptr<ANode> end) const
  48. {
  49. return point->G + calculateH(point, end);
  50. }
  51. void AStar::HeapSort(int beg, int end)
  52. {
  53. auto temp = openlist[beg];
  54. int pre = beg;
  55. for (int i = pre * 2 + 1; i <= end; i = i * 2 + 1)
  56. {
  57. if (i + 1 <= end && openlist[i + 1]->F < openlist[i]->F)
  58. ++i;
  59. if (openlist[i]->F <= temp->F)
  60. openlist[pre] = openlist[i];
  61. else
  62. break;
  63. pre = i;
  64. }
  65. openlist[pre] = temp;
  66. }
  67. void AStar::HeapSort(int end)
  68. {
  69. int pre = int(end) - 1;
  70. int i = ((int(end) - 1) % 2 == 0) ? (int(end) - 1) / 2 - 1 : (int(openlist.size()) - 1) / 2;
  71. for (; i >= 0; i = (i % 2 == 0) ? i / 2 - 1 : i / 2)
  72. {
  73. if (openlist[i]->F >= openlist[pre]->F)
  74. std::swap(openlist[i], openlist[pre]);
  75. else
  76. break;
  77. pre = i;
  78. }
  79. }
  80. std::shared_ptr<ANode> AStar::findPath(std::shared_ptr<ANode> beg, std::shared_ptr<ANode> end)
  81. {
  82. refreshOpenList(beg,end);
  83. while (!openlist.empty())
  84. {
  85. auto iter2 = std::find_if(openlist.cbegin(), openlist.cend(), [end](std::shared_ptr<ANode> sp)
  86. { return (sp->x == end->x) && (sp->y == end->y); });
  87. if (iter2 != openlist.end())
  88. return *iter2;
  89. auto iter_temp = openlist.front();
  90. std::swap(openlist[0], openlist[openlist.size() - 1]);
  91. openlist.pop_back();
  92. HeapSort(0,openlist.size()-1);
  93. closeist.push_back(iter_temp);
  94. refreshOpenList(iter_temp, end);
  95. }
  96. return nullptr;
  97. }
  98. void AStar::refreshOpenList(std::shared_ptr<ANode> point, std::shared_ptr<ANode> end)
  99. {
  100. bool upIsWall = false;//表示当前点上有障碍物,即对应的斜向没法走
  101. bool downIsWall = false;
  102. bool leftIsWall = false;
  103. bool rightIsWall = false;
  104. if (point->x - 1 >= 0 && maps[point->x - 1][point->y] == 1)
  105. upIsWall = true;
  106. if (point->x + 1 < int(maps.size()) && maps[point->x + 1][point->y] == 1)
  107. downIsWall = true;
  108. if (point->y - 1 >= 0 && maps[point->x][point->y - 1] == 1)
  109. leftIsWall = true;
  110. if (point->y + 1 < int(maps.front().size()) && maps[point->x][point->y + 1] == 1)
  111. rightIsWall = true;
  112. //第一步初始化起点及其周围的节点
  113. if (openlist.empty() && closeist.empty())
  114. {
  115. closeist.push_back(point);
  116. for (int i = point->x - 1; i <= point->x + 1; ++i)
  117. {
  118. for (int j = point->y - 1; j <= point->y + 1; ++j)
  119. {
  120. if (i >= 0 && j >= 0 && i < int(maps.size()) && j < int(maps.front().size()) && (i != point->x || j != point->y) && !maps[i][j])
  121. {
  122. if (i != point->x && j != point->y)
  123. {
  124. if (leftIsWall && j < point->y)
  125. continue;
  126. if (rightIsWall && j > point->y)
  127. continue;
  128. if (upIsWall && i < point->x)
  129. continue;
  130. if (downIsWall && i > point->x)
  131. continue;
  132. }
  133. auto cur = std::make_shared<ANode>(i, j, point);
  134. cur->G = (i != point->x && j != point->y) ? costHigh : costLow;
  135. cur->H = calculateH(cur, end);
  136. cur->F = calculateF(cur, end);
  137. openlist.push_back(cur);
  138. }
  139. }
  140. }
  141. int i = ((int(openlist.size()) - 1) % 2 == 0) ? (int(openlist.size()) - 1) / 2 - 1 : (int(openlist.size()) - 1) / 2;
  142. for (; i >= 0; --i)
  143. HeapSort(i, openlist.size() - 1);
  144. }
  145. //刷新每一次找到的起点到当前点的最短路径中的当前点的周围节点情况
  146. else
  147. {
  148. for (int i = point->x - 1; i <= point->x + 1; ++i)
  149. {
  150. for (int j = point->y - 1; j <= point->y + 1; ++j)
  151. {
  152. if (i >= 0 && j >= 0 && i < int(maps.size()) && j < int(maps.front().size()) && (i != point->x || j != point->y) && !maps[i][j])
  153. {
  154. if (i != point->x && j != point->y)
  155. {
  156. if (leftIsWall && j < point->y)
  157. continue;
  158. if (rightIsWall && j > point->y)
  159. continue;
  160. if (upIsWall && i < point->x)
  161. continue;
  162. if (downIsWall && i > point->x)
  163. continue;
  164. }
  165. auto cur = std::make_shared<ANode>(i, j, point);
  166. cur->G = ((i != point->x && j != point->y) ? costHigh : costLow) + point->G;
  167. cur->H = calculateH(cur, end);
  168. cur->F = calculateF(cur, end);
  169. auto iter_close=std::find_if(closeist.cbegin(), closeist.cend(), [i,j](std::shared_ptr<ANode> sp)
  170. { return (sp->x == i) && (sp->y == j); });
  171. if (iter_close == closeist.end())
  172. {
  173. auto iter_open=std::find_if(openlist.cbegin(), openlist.cend(), [i,j](std::shared_ptr<ANode> sp)
  174. { return (sp->x == i) && (sp->y == j); });
  175. if (iter_open != openlist.end())
  176. {
  177. if((*iter_open)->G > cur->G)
  178. {
  179. (*iter_open)->G = cur->G;
  180. (*iter_open)->F = (*iter_open)->G + (*iter_open)->H;
  181. (*iter_open)->prev = point;
  182. HeapSort(int(iter_open - openlist.begin()) + 1);
  183. }
  184. }
  185. else
  186. {
  187. openlist.push_back(cur);
  188. HeapSort(int(openlist.size()));
  189. }
  190. }
  191. }
  192. }
  193. }
  194. }
  195. }
  196. void AStar::PrintAStarPath(const std::pair<int, int>& start, const std::pair<int, int>& end)
  197. {
  198. auto start_sp = std::make_shared<ANode>(start.first, start.second), end_sp = std::make_shared<ANode>(end.first, end.second);
  199. std::shared_ptr<ANode> cur = findPath(start_sp, end_sp);
  200. if (!cur)
  201. std::cout << "没有找到起点到终点路径" << std::endl;
  202. else
  203. {
  204. while (cur)
  205. {
  206. maps[cur->x][cur->y] = '*';
  207. cur = cur->prev.lock();
  208. }
  209. for (const auto &i : maps)
  210. {
  211. for (const auto &j : i)
  212. {
  213. if (j > 1)
  214. std::cout << char(j);
  215. else
  216. std::cout << j;
  217. }
  218. std::cout << std::endl;
  219. }
  220. }
  221. }

主函数文件

  1. #include"AStar.h"
  2. int main()
  3. {
  4.    std::vector<std::vector<int>> map = {{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
  5.                                         {1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1},
  6.                                         {1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1},
  7.                                         {1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1},
  8.                                         {1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1},
  9.                                         {1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1},
  10.                                         {1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1},
  11.                                         {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};
  12.    AStar star(map);
  13.    star.PrintAStarPath({1, 1}, {6, 10});
  14.    return 0;
  15. }

​最终结果

小型地图:

其中1表示障碍物,0表示可通的道路

在大型地图例如6000*6000的地图,找(0,0)到(5999,5999)的路径时候:

        改进后的算法用时17545ms 改进前的为28566ms

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/526210
推荐阅读
相关标签
  

闽ICP备14008679号